用户:Xyy23330121/Python/类的方法与类实例
实例创建 编辑
我们之前已经提到过 __init__
方法,这里将详细讲解创建实例的过程。
object.__new__(cls[, ...]) 编辑
def __new__(cls, value="0", context=None):
self = object.__new__(cls)
...
if isinstance(value, str):
...
self._sign = 1
...
return self
可以看出,自定义的 __new__ 方法,它满足以下特征:
- 第一步就是用 object(所有类的父类)定义的 __new__ 方法,把结果赋值给变量 self。
- 之后是对 self 创建各种属性,由于使用的变量名称是 self,所以语法和 __init__(self) 中完全一致。
- 最后 return self
本方法是创建实例的第一步。它会在使用时创建一个新实例。
本方法默认为静态方法、类方法(而无需用装饰器),它会将所属的类自动传入第一个参数。
在自定义该方法时,一般会使用 super().__new__
访问父类定义的 __new__
方法,并修改父类 __new__
方法返回的结果。
super(type, object_or_type=None) 编辑
此函数[2]会返回一个特殊对象。任何对该对象的方法都会被重定向到 type
的父类中定义的方法。比如以下示例:
class C:
def method(self): print("C.method()")
class D(C):
def method(self): print("D.method()")
def usemethod(self):
self.method()
super().method()
obj = D()
obj.usemethod()
输出为:
D.method()
C.method()
从而,在子类中也可以调用已经被子类作用域所覆盖的、父类的方法。
该方法查找父类方法的顺序,和子类继承父类方法的顺序是一致的(但跳过了调用super()
的子类本身)。
object.__init__(self[, ...]) 编辑
如果 __new__
方法在构造对象期间被发起调用,并返回了一个 cls 的实例。则新实例的 __init__
方法将会被调用。调用时,会传入所有传入 __new__ 方法的参数。比如以下示例:
class test:
def __new__(cls, value):
print(value)
return object.__new__(test)
def __init__(self, value):
print(value)
a = test("传入的参数")
该实例输出两遍 传入的参数
,可见传入 __new__ 的 value 参数也被传入了 __init__ 的 value 参数。
__init__
方法直接修改 __new__
方法返回的实例,只要它返回除了None
以外的任何值,就会导致错误。
实例属性 编辑
获取实例属性 编辑
object.__getattribute__(self, name) 编辑
此方法是调用实例属性时使用的方法。自定义此方法会影响对已有属性的读取。
该方法应该返回找到的属性值,或引发 AttributeError
。
由于只要使用 self.name
访问属性就会调用该方法。自定义该方法时,为了避免无限递归,应当注意不要使用 self.name
的方式来访问实例的属性,而是使用其基类的方法 object.__getattribute__(self, name)
来访问实例属性。
object.__getattr__(self, name) 编辑
当 __getattribute__
方法引发 AttributeError
时,会调用此方法再次尝试获取属性。因此,自定义此方法不会影响对已有属性的读取。
该方法应该返回找到的属性值,或引发 AttributeError
。
由于本方法是在 __getattribute__
方法之后调用,只要 __getattribute__
方法能找到对应的属性,本方法是可以使用 self.name
来读取属性的。
设置实例属性 编辑
object.__setattr__(self, name, value) 编辑
此方法用于支持 self.name = value
的操作。和获取实例属性时不同,设置实例属性只有这一个函数。
类似 __getattribute__
,在自定义该方法时,应当注意不要使用 self.name = value
的形式。如果有需要,就调用基类的方法 object.__setattr__(self, name, value)
。
object.__delattr__(self, name) 编辑
此方法用于支持 del self.name
的操作。
实例属性与方法列表 编辑
object.__dir__(self) 编辑
该方法用于支持 dir(self)
的操作。它会返回实例中、属性与方法名称的字符串组成的列表。
inspect 模块 编辑
inspect 模块列出了所有可获得的内置特殊属性。详情请参见 Python 文档。
描述器 编辑
我们之前学习“私有量”的时候提到过 property。property 是一种描述器。以下所示的方法仅当它作为类的属性时才会起作用。
这是因为获取、设置或删除类的属性时,类的 __getattribute__ 等方法会尝试调用描述器的方法。描述器如果不作为类的属性、而作为一般的变量使用时,不会有 __getattribute__ 等方法来调用描述器方法。
只要设置了下面方法中的一个,该类型就是一种描述器。
object.__get__(self, instance, owner=None) 编辑
其中,instance 是描述器属性所在的实例,而 owner 是 instance 所属的类型。
object.__set__(self, instance, value) 编辑
其中,instance 是描述器属性所在的实例,而 value 是要赋的值。
object.__delete__(self, instance) 编辑
其中,instance 是描述器属性所在的实例。
我们看以下示例:
class ReadOnly:
def __init__(self, value):
self.__value = value
def __get__(self, instance, owner=None):
print(instance.name, "and", owner.clsname)
return self.__value
def __set__(self, instance, value):
raise AttributeError("readonly attribute")
def __delete__(self, instance):
raise AttributeError("readonly attribute")
class C:
clsname = "Class C"
def __init__(self, name):
self.name = name
x = ReadOnly(0)
c = C("Instance c")
output = c.x #输出:Instance c and Class C
print(output) #输出:0
c.x = 2 #报错:AttributeError: readonly attribute
object.__objclass__ 编辑
在创建描述器时,还可以添加一个可选的属性。该属性应当设置为描述器属性所在的实例类型。该属性会被 inspect 模块所使用,有助于动态类属性的运行时内省。
子类创建 编辑
为了代码的易于扩展,有时需要自定义子类创建的行为。
object.__init_subclass__(cls[, ...]) 编辑
在创建子类时会调用父类的此方法,其中, cls 参数会被自动传入新的子类。
这里的参数是在创建类的时候,通过“关键词参数”方法传入的,比如:
class test:
def __init_subclass__(cls, arg=0):
print(arg)
pass
class test1(test, arg="传入的参数"):
pass
以上示例的输出是传入的参数
。如果使用位置参数,则会与“父类的继承”产生混淆。
此方法是子类被创建完毕后,对子类作修改。而非从头创建子类。此方法返回任何值都会u报错。
示例 编辑
random.Random[3]类就有此方法,其代码节选如下:
def __init_subclass__(cls, /, **kwargs):
for c in cls.__mro__:
if '_randbelow' in c.__dict__:
# just inherit it
break
if 'getrandbits' in c.__dict__:
cls._randbelow = cls._randbelow_with_getrandbits
break
if 'random' in c.__dict__:
cls._randbelow = cls._randbelow_without_getrandbits
break
random.Random 类对于随机数有不同的处理。有的方法使用随机整数以规避浮点误差,而有的方法使用随机浮点数。其中,使用随机整数的方法调用的是 _randbelow 方法。
此处 __init_subclass__ 的处理,在子类定义了 getrandbits (随机整数方法)的情况下,让 _randbelow 变为 _randbelow_with_getrandbits 方法,再在 _randbelow_with_getrandbits 中调用 getrandbits 方法。从而实现了随机整数方法的调用。
如果子类没有定义 getrandbits,则查询是否定义了 random (随机浮点数方法)。如果有,让 _randbelow 变为 _randbelow_without_getrandbits 方法,再在 _randbelow_without_getrandbits 方法中调用 random 方法,并把结果转化为整数。
如果子类两者都没有定义,则认为子类没有修改随机数生成器,此时按继承顺序继续查找父类的方法。
通过这样的处理,子类可以只定义 random 方法就修改 random.Random 中所有类型随机数的生成,也可以附加定义 getrandbits 来为整数作进一步优化。增加了代码的可扩展性。
object.__set_name__(self, owner, name) 编辑
在创建类的同时创建属性时会调用此方法。我们看以下示例:
class AttrTest:
def __set_name__(self, owner, name):
print(self, owner, name)
def __str__(self):
return "AttrTest 的实例"
__repr__ = __str__
attr = AttrTest()
class test:
x = attr #在创建类的同时创建属性
test.y = attr #在创建类之后,再创建属性
attr.__set_name__(test,"z") #手动调用该方法
print(test.x, test.y)
print(test.z)
输出为:
AttrTest 的实例 <class '__main__.test'> x
AttrTest 的实例 <class '__main__.test'> z
AttrTest 的实例 AttrTest 的实例
Traceback (most recent call last):
File "...", ..., in <module>
print(test.z)
^^^^^^
AttributeError: type object 'test' has no attribute 'z'