神奇的类
Descriptor
TODO
Python 有三个内置 descriptor(也是 decorator):@property、@classmethod 和 @staticmethod。
Property
使用 @property 可以让我们建立一个需要 method 计算的 field。例如对一个正方形,我们可以只存储边长,而将面积设置为一个 property,需要的时候即时计算。
例子来自《Learning Python》。
class Person:
def __init__(self, name):
# 通常用带下划线的名字作为实际值
self._name = name
@property
def name(self): # name = property(name)
"""name property docs"""
print('fetch...')
return self._name
# setter 和 deleter 的名字也都是 name
@name.setter
def name(self, value): # name = name.setter(name)
print('change...')
self._name = value
@name.deleter
def name(self): # name = name.deleter(name)
print('remove...')
del self._name
Static method
使用 @staticmethod,则参数不会添加 self 或 cls,就像一个 function 一样,只是在一个类内。
class Book:
@staticmethod
def foo(x):
return x
Class method
class Book:
@classmethod
def foo(cls, x):
return cls, x
Data classes
我们经常会写:
class Book:
def __init__(self, name: str, author: str):
self.name = name
self.author = author
用 dataclasses 的话,只需要:
from dataclasses import dataclass
@dataclass
class Book:
name: str
author: str
@dataclass 会帮我们自动加上 __init__()、__repr__()、__eq__() 等。
用处一是简化类的代码,二是可以替代 namedtuple、typing.NamedTuple、attrs 等。有一点像 Typescript 的 interface。
讨论:https://stefan.sofa-rockers.org/2020/05/29/attrs-dataclasses-pydantic/
特殊 attribute
class Book:
'''A book'''
name: str = 'A name'
# 类的特殊 attribute
__name__ # 类名 'Book'
__module__ # 类所在模块 '__main__'
__dict__ # 类的命名空间 mappingproxy({'__module__': '__main__', '__annotations__': {'name': <class 'str'>}, '__doc__': 'Book class', 'name': 'Good', '__dict__': <attribute '__dict__' of 'Book' objects>, '__weakref__': <attribute '__weakref__' of 'Book' objects>})
__bases__ # 基础类 (<class 'object'>,)
__doc__ # 文档 'A book'
__annotations__ # 属性的标注,若如则无此 attribute {'name': <class 'str'>}
# 类的特殊方法
object.__new__(cls, ...) # 这其实是一个静态方法。创建对象时,先调用 cls.__new__(),再调用 self.__init__()
object.__init__(self, ...)
object.__del__(self) # 在实例被摧毁前调用,注意 del x 并不调用 x.__del__()
object.__repr__(self) # repr() 调用该方法,返回一个 str,主要用于 debugging,比如在交互式 shell 里,给程序员看
object.__str__(self) # str(object)、format()、print() 调用该方法,可以给最终用户看
object.__bytes__(self) # bytes() 调用该方法
object.__format__(self, format_spec) # format() 调用该方法
object.__lt__(self, other) # <,这些都是用于和另一个对象大小、相等
object.__le__(self, other) # <=
object.__eq__(self, other) # ==,如果没有定义,默认使用 is 来判断 __eq__()
object.__ne__(self, other) # !=,如果没有定义,默认使用 !__eq__()
object.__gt__(self, other) # >
object.__ge__(self, other) # >=
object.__hash__(self) # hash() 调用该方法,应保证 __eq__() 的对象的 hash 相同;如果没有定义 __eq__(),不要定义该方法
object.__bool__(self) # bool() 调用该方法,如果没有定义,只要 __len__() 不是 0 则为 True;若 __len__() 没有定义,则永远为 True
# 自定义类的 attribute 的访问
object.__getattribute__(self, name) # 默认 attribute 访问机制;一般不重写此方法
object.__getattr__(self, name) # 这三个是可以自定义的方法,作为 fallback
object.__setattr__(self, name, value)
object.__delattr__(self, name)
object.__dir__(self) # dir() 调用该方法,该方法应返回 sequence;dir() 会将结果转为 list 并排序;一般不重写此方法
# TODO descriptor
object.__get__()
object.__set__()
object.__delete__()
# TODO
object.__slots__()
类的 attribute 访问机制
Managing Attribute Access and Descriptors (Theory of Python) (Python Tutorial) - YouTube