枚举类

Python 提供了 enum 模块的枚举类 Enum,只需要继承这个类,里面的类属性就都变成 iterable 类型的了。

然后就可以使用循环访问了。

不同的是,枚举类无法修改成员,并且类属性名字不能重复。

1
2
3
4
5
6
7
8
9
10
from enum import Enum  
class Xorex(Enum):
name = "Xorex"
grage = "Tempest"
age = 18
Length = 18
# 上面两个 age 和 Length 因为值相同,会被看做成一个

for i in Xorex:
print(i) #只会输出前三个,Length 作为 age 的别名

特别的

直接写代码

这里的直接写代码就是在类定义下面直接写,等价于 Java 的静态代码块,会在 加载类代码的时候运行。毕竟类下面定义的,就是这个类命名空间下面的代码嘛,自然遇到能执行的就执行,函数的话就封装起来嘛。

所以这里也不难理解,为什么直接写在类里面的变量,就等同于静态变量了……

调用 类名方法之后,解释器创建一个对象(用来保存实例属性和实例所属类的结构体),然后调用 __init__ 方法并将这个对象(结构体)传入 self 中。执行完方法之后,self 作为返回值返回给调用处的地方。

一般来说,会在构造方法中,初始化类所需要的实例属性。

描述符

Python 的描述符是一个特殊的类,这个类用于管理某一种属性的 getter 和 setter。举两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class NameControl: # 用来控制名字 name 的 setter 和 setter  
def __init__(self, name: str = "HAHA"):
self.name = name

def __get__(self, instance, owner): # 规定的 getter 方法
return self.name

def __set__(self, instance, value):
if not isinstance(value, str):
print("要输入字符串哦!")
else:
self.name = value

class People:
name = NameControl() # 绑定到类属性上面
  • 不理解为什么描述符是绑定在 类属性 上面。

  • 类属性最初可以激活 getter 方法,但是无法激活 setter 方法

  • 实例属性最初都可以激活 getter 和 setter

  • 当对类属性进行赋值之后,描述符就失效了。应该是赋的值覆盖了绑定符地址。

  • 一个类的多个实例,使用的描述符对象竟然是同一个???

猜测应该是通过实例属性保存 描述符 实例,然后对实例访问和赋值就是对描述符进行访问赋值,会激活特殊方法 __get__ __set__,从而通过描述符来实现对属性访问的控制。

对描述符里面的特殊方法的调用,是在 __getattribute__() 中决定调用的,判断的逻辑如下:

  1. 验证该属性是否为类实例对象的数据描述符;
  2. 如果不是,就查看该属性是否能在类实例对象的 __dict__ 中找到;
  3. 最后,查看该属性是否为类实例对象的非数据描述符。

property() 函数

这个函数的作用和 描述符 类似。不过它是手动绑定起到 getter 和 setter 的方法,绑定方法为: 属性名=property(fget=None, fset=None, fdel=None, doc=None) 这样以后在对 属性名 进行访问和赋值的时候,都会经过 property 绑定的方法完成。

  • fget: getterFunction
  • fset: setterFunction
  • fdel: deleteFunction
  • doc: 字符串,属性描述信息。
1
2
3
4
5
6
7
8
9
10
11
12
class Name:  
def __init__(self, name: str = ""):
self.__name = name # 记得要设置成私有属性 __name,因为如果名字还是 name 的话,就会无限触发绑定的 getName 方法,进入死调用循环。

def getName(self):
return self.__name

def setName(self, name: str):
self.__name = name
return

name = property(getName,setName) # 绑定到类属性 name 上面

然后就可以随心所欲的在访问实例属性的时候,全都经过指定的 getter setter 方法了!!!

类特殊成员

Python 类中的特殊成员名字都是用下划线包起来的,这些特殊的成员在 Python 解释器下面会有特殊的作用。

不像是 Java 的那种没有特殊标识,比如 toString() 构造方法,iterator() 方法等,Python 的特殊方法一眼都可以看出来。

下面的特殊方法,如果不需要自己声明就可以调用,那么就一定是基类 object 里面包含有的!

__init__() 构造方法

这里的构造方法和 Java 中是一样的,不过因为 Python 支持多继承以及不支持重载,所以在继承中,不会自动调用父类的构造方法,这里需要自己手动调用。

要么用 super().__init__() 要么 用 类名.__init__(self)

__repr__() 描述方法

represent 类描述方法,在 object 中输出的是当前对象的 类名+object at+内存地址。可以重写,在遇到需要转化为字符串的时候,如果没有 __str__() 方法,则会调用 __repr__(),否则优先调用 __str__()

__str__() 字符串

需要返回一个字符串,作为对象需要被转化为字符串类型的时候的值。

__del__() 销毁对象

对于 Python 来说,使用 del XXX 语句可以将对象的引用计数减一,减到零的时候,就会触发 __del__() 方法进行善后,善后完毕后清除内存。

需要注意的是,__del___() 就是一个善后的,它并不负责引用计数器减一和清除内存的工作,它只是被调用的。

另外,一定要记得在子类的 __del__() 中调用父类的 __del__() 来完全完成善后工作。因为父类并不单独存在对象,所以自然只调用 __del__() 即可。

__dir__() 列出成员

用来列出当前实例所持有的所有成员(包括继承来的属性和方法)。

和 dir() 的作用类似,不过区别是 dir() 可以列出来类和实例的成员,而 __dir__() 只能列出来实例的成员。

__dict__ 字典属性

  • 对于类属性 __dict__:以字典的形式输出所有的类属性。
  • 对于实例属性 __dict__: 以字典的形式输出所有的实例属性。

__call__() 调用绑定

在类中可以定义一个名字叫做 __call__() 的方法,这个方法会和 实例名() 这样的调用方式绑定,好玩:

1
2
3
4
5
6
7
class Xorex:  
def __call__(self, name): # 将方法和实例名绑定,可以通过实例名调用此方法
print(name + " called the Xorex.")

xorex = Xorex()
xorex.__call__("Yukino") # 手动调用
xorex("Asuna") # 绑定调用

和类相关的内建函数

对于 Python 中的各种不需要 import 就可以使用的东西,比如各种方法,以及类 object 等,都是包含在内建模块中 builtins 里面。在运行 Python 代码之前会优先加载这个内建模块。

hasattr()

hasattr(obj, name) 返回布尔类型,判断是否有名字为 name 的方法或者属性。

getattr()

getattr(obj, name) 如果找不到 name 那就跑出来异常。如果是属性,返回属性值,如果是方法,返回方法信息。

setattr()

setattr(obj, name, value) 这个就是设置属性了。适合只有字符串的那种批量建立属性。

类型检查

issubclass(cls, class_or_tuple) 判断是否为子类,可以跟着一个 tuple 遍历判断。

isinstance(obj, class_or_tuple) 判断是否为实例,可以跟着一个 tuple 遍历判断。

运算符重载

运算符重载是作用与对象进行运算符计算的时候,应该调用什么方法来完成:

数值运算:

重载方法 重载运算 运算
__add__(self,rhs) self + rhs 加法
__sub__(self,rhs) self - rhs 减法
__mul__(self,rhs) self * rhs 乘法
__truediv__(self,rhs) self / rhs 除法
__floordiv__(self,rhs) self //rhs 地板除
__mod__(self,rhs) self % rhs 取模(求余)
__pow__(self,rhs) self **rhs 幂运算

逻辑运算:

重载方法 重载运算 运算
__lt__(self,rhs) self < rhs Less Than
__gt__(self,rhs) self > rhs Grater Than
__le__(self,rhs) self <= rhs Less Equal
__ge__(self,rhs) self >= rhs Grater Equal
__eq__(self,rhs) self = rhs Equal
__ne__(self,rhs) self != rhs Nagtive Equal

装饰器原理

我们之前是见过装饰器的,就是 @staticmethod @classmethod 这些。而它们是什么呢,可以干什么呢。

其实就是通过设计模式——装饰器来修改原有函数的功能,变成一个加强版本的函数:

1
2
3
4
5
6
7
8
9
10
11
12
def logger(Func): #定义装饰器,会将被装饰的函数当作实参传入  
def wapper(*args,**kwargs): #对传入的函数进行包装为新的此函数
print("[logger] Invoke Func: "+Func.__name__)
print("[logger] Args are "+args.__str__()+' '+kwargs.__str__())
return Func(*args,**kwargs)
return wapper #将包装好的函数地址返回给原函数名

@logger #这个等价于函数后面多了一句话: hello = logger(hello)
def hello(name):
print("Ya hello! "+name)

hello("Xorex") #调用被包装之后的函数

上面代码的核心就是把 @logger 标注在函数 hello 上面之后,会自动生成一句代码到 hello 后面:hello = logger(hello) 所以我们要在 logger 里面定义装饰器的代码,将 hello 函数装饰之后返回一个新的被装饰过的函数。


允许标注多个装饰器,执行顺序从上到下:

1
2
3
4
5
@funA  
@funB
@funC
def fun():
pass

则会这样执行:fun = funA( funB ( funC (fun) ) )