天天在写python,但是感觉自己对python的高级用法不是特别了解,此处浅析一下。

1 python类与对象

1.1 专业的类书写规范

类中比较专业的规范为:
  • 1 类属性
  • 2 __init__初始化方法
  • 3 实例属性
  • 4 classmethod方法
  • 5 staticmethod方法
  • 6 私有方法
  • 7 实例方法
classmethod方法与staticmethod方法:
  • 1 classmethod方法涉及使用类中的资源
  • 2 staticmethod方法一般用来实现固定的操作,一般不涉及类中的资源的操作。
举例,我们写一个学生信息类,将以上的方法都实现一遍。
class Student(object):
    # 1 类属性
    _ID_TYPE: str = "Student Person"

    # 2 __init__初始化方法
    def __init__(self, name, score, age):
        # 3 实例属性
        self._name: str = name
        self._score: int = score
        self._age: int = age

    # 4 classmethod方法: 能操作类的全部资源
    @classmethod
    def get_student(cls):
        return cls("tom",  "100", 18)

    # 5 staticmethod方法: 只能操作类的实例
    @staticmethod
    def print_student_type():
        print(f"name is {Student._ID_TYPE}")

    # 6 私有方法: 对外层用户透明
    def _set_age(self, age):
        if age > 100:
            self._age = 100
        elif age < 0:
            self._age = 0
        else:
            self._age = age

    # 7 实例方法
    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, score):
        self._score = score

    def set_age(self, age):
        self._set_age(age)

1.2 类的私有成员保护

python实际上没有私有成员,大家公认的是实例或者类的私有成员(变量和方法)在前面加上下划线(IDE将不会帮助提示)。 如果类或者实例中的属性不设置私有,则其他程序员可以通过赋值的方式修改属性。 解决方法:将属性设置为私有属性,同时提供@proptity提供访问接口。 实现对私有成员的访问,同时不提供修改的策略。

1.3 抽象类

抽象类继承abc.ABC,同时设置了@abstractmethod的方法子类必须重写。

1.4 魔法方法

类的函数中左右两边都存在两个下划线的即为魔法方法。

1.5 __str__和__repr__

student = Student.get_student()

print(student)
此时对象的print的结果就会去__str__函数进行处理。
student1 = Student.get_student()
student2 = Student.get_student()

print([student1, student2])
在列表、元组、集合中打印的对象会去__repr__函数进行处理。

2 装饰器

装饰器分为两部分:
  • 1 装饰器的输入和输出
    • 输入:oldFunction
    • 输出:newFunction
  • 2 装饰器的加工过程
    • newFunction对oldFunction进行加工

2.1 不带参数的装饰器

  • 1 装饰器输入一个旧函数,抛出一个新函数
  • 2 装饰器在新函数中对旧函数进行加工
def robot(oldFunc):
    def newFunc():
        print("start")
        oldFunc()
        print("end")
    return newFunc

@robot
def hello():
    print("Hello World")

hello()
# start
# Hello World
# end

2.2 带参数的装饰器

  • 1 装饰器输入一个旧函数,抛出一个新函数
  • 2 装饰器在新函数中对旧函数进行加工
  • 3 装饰器的新函数入口的参数设置与旧函数保持一致才能进行加工。
def robot(oldFunc):
    def newFunc(name):
        oldFunc(name[::-1])
    return newFunc

@robot
def hello(name):
    print(f"Hello {name}")

hello("alice")
# Hello ecila

2.3 多装饰器

多装饰器的执行顺序为离近原则,从离函数近到远的顺序执行装饰器。 例如,我们给出@runDouble, @logger装饰器.
2.3.1 @logger @runDouble
def runDouble(oldFunc):
    def newFunc(*args, **kwargs):
        oldFunc(*args, **kwargs)
        oldFunc(*args, **kwargs)

    return newFunc


def logger(oldFunc):
    def newFunc(*args, **kwargs):
        print(f"===============")
        oldFunc(*args, **kwargs)
        print(f"===============")

    return newFunc
@runDouble
@logger
def hello(name):
    print(f"Hello {name}")

hello("alice")

# ===============
# Hello alice
# ===============
# ===============
# Hello alice
# ===============
2.3.2 @runDouble @logger
def runDouble(oldFunc):
    def newFunc(*args, **kwargs):
        oldFunc(*args, **kwargs)
        oldFunc(*args, **kwargs)

    return newFunc


def logger(oldFunc):
    def newFunc(*args, **kwargs):
        print(f"===============")
        oldFunc(*args, **kwargs)
        print(f"===============")

    return newFunc

@logger
@runDouble
def hello(name):
    print(f"Hello {name}")

hello("alice")

#===============
# Hello alice
# Hello alice
# ===============

2.4 双重装饰器(带括号)

双重装饰器(带括号)实际上就是可以等同于使用两次@装饰器修饰. 双重装饰器的实现需要使用2次函数嵌套,具体如下:

def fun(type):
    if type == "double":
        def runDouble(oldFunc):
            def newFunc(*args, **kwargs):
                oldFunc(*args, **kwargs)
                oldFunc(*args, **kwargs)

            return newFunc
        return runDouble
    elif type == "logger":
        def logger(oldFunc):
            def newFunc(*args, **kwargs):
                print(f"===============")
                oldFunc(*args, **kwargs)
                print(f"===============")

            return newFunc
        return logger


@fun("double")
def hello(name):
    print(f"Hello {name}")

hello("alice")

# Hello alice
# Hello alice

3 浅拷贝与深拷贝

3.1 普通赋值

a = [1, 2, 3] b = a 修改a,则b也会跟着修改。 因为a,b都指向同一个对象,即指向同一块内存地址。
a = [1, 2, 3]
b = a
a[0] = 4
print(a, b)

# [4, 2, 3] [4, 2, 3]

3.2 浅拷贝

3.2.1 不带子对象的浅拷贝
a = [1, 2, 3] b = a.copy() 修改a,b不会跟着修改。 b为一个新的对象,拷贝了a的父对象。
a = [1, 2, 3]
b = a.copy()
a[0] = 4
print(f"a is {a} b is {b}")

# a is [4, 2, 3] b is [1, 2, 3]
3.2.2 带子对象的浅拷贝
a = [1, 2, [3, 4]] b = a.copy() 修改a[2]中的列表值,b跟着修改。 b虽然为一个新的对象,拷贝了a的父对象,但是不会拷贝子对象[3, 4].
a = [[1, 4, 5], 2, 3]
b = a.copy()
a[0][0] = 9
print(f"a is {a} b is {b}")

# a is [[9, 4, 5], 2, 3] b is [[9, 4, 5], 2, 3]

3.3 深拷贝

from copy import deepcopy a = [1, 2, [3, 4]] b = deepcopy(a) 修改a[2]的列表,b不发生改变。 b拷贝了a的父对象和子对象。
import copy

a = [[1, 4, 5], 2, 3]
b = copy.deepcopy(a)
a[0][0] = 9
print(f"a is {a} b is {b}")

# a is [[9, 4, 5], 2, 3] b is [[1, 4, 5], 2, 3]

4 如何多层跳出循环

众所周知,break只能跳出一层循环,如果遇到多层循环该如何直接退出到最外层循环呢。

4.1 try except

最推荐使用的一种方法,无论在多少层for之中,最内层抛出一个raise给except接受即可。
try:
    for i in range(10):
        for j in range(10):
            print(f"i is {i} j is {j}")
            if (i == j == 2):
                raise "i,j已经满足要求了"
except Exception as e:
    print(e)

4.2 for else continue

不太推荐使用,遇到多个for则需要写多个else: continue break else在这里处于一种语法糖一样的东西
  • 1 当for循环执行成功,则执行else中的语句
  • 2 当for循环执行不成功,则跳过else中的语句。
  • 3 此时,当时for循环中遇到break,则跳过else中的continue,继续执行break。
for i in range(10):
    for j in range(10):
        print(f"i is {i} j is {j}")
        if (i == j == 2):
            break
    else:
        continue
    break

5 any和all

5.1 简易使用

any:列表中有一个为True,则返回True all:列表中有一个为False,则返回False
print(any([True, False, True, False]))
print(all([True, False, True, False]))
# True
# False

5.2 搭配列表使用

判断一个列表中是否存在能整除2的数。
x = [1, 2, 3, 4, 5, 6]
print(any([i%2==0 for i in x]))