【python】魔术方法大全(三)-- 属性篇
这次我们主要来聊聊跟对象的属性有关的魔术方法。
基础属性魔法方法
__getattr__魔术方法
__getattr__ 是 Python 中的一个特殊方法,用于在访问一个不存在的属性时被调用。如果一个对象定义了 __getattr__ 方法,那么当它的属性被访问时,如果该属性不存在,则 Python 会调用该对象的 __getattr__ 方法来处理这个访问。
以下是一个简单的示例,演示了如何使用 __getattr__ 方法:
class Example:
def __init__(self):
self.data = {'a': 1, 'b': 2, 'c': 3}
def __getattr__(self, name):
if name in self.data:
return self.data[name]
else:
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
在这个示例中,我们定义了一个 Example 类,它有一个 data 属性,其中包含一些数据。在 __getattr__ 方法中,我们检查属性名称是否存在于 data 字典中。如果是,我们返回相应的值;否则,我们抛出一个 AttributeError 异常,表示该属性不存在。
现在,我们可以创建一个 Example 实例,并访问它的属性,即使这些属性并不存在:
>>> ex = Example()
>>> ex.a
1
>>> ex.b
2
>>> ex.c
3
>>> ex.d
AttributeError: 'Example' object has no attribute 'd'
在这个示例中,我们首先创建了一个 Example 实例,然后访问了它的属性 a、b 和 c,它们都存在于 data 字典中。然后我们试图访问属性 d,这个属性并不存在,于是 Python 调用了 __getattr__ 方法,并抛出了一个 AttributeError 异常。
需要注意的是,如果一个对象同时定义了 __getattr__ 和 __getattribute__ 方法,那么 __getattribute__ 方法会比 __getattr__ 方法更先被调用,因为它处理所有属性的访问,而不仅仅是不存在的属性。因此,如果您要使用 __getattr__ 方法,需要确保它只用于处理不存在的属性。
__getattribute__魔术方法
__getattribute__ 是 Python 中的一个特殊方法,用于在访问对象属性时被调用。当我们使用点号(.)或 getattr() 函数来访问一个对象的属性时,Python 会调用该对象的 __getattribute__ 方法来获取属性的值。与 __getattr__ 方法不同的是,__getattribute__ 方法对于对象的所有属性都会被调用,而不仅仅是不存在的属性。
以下是一个简单的示例,演示了如何使用 __getattribute__ 方法:
class Example:
def __init__(self):
self.data = {'a': 1, 'b': 2, 'c': 3}
def __getattribute__(self, name):
try:
return object.__getattribute__(self, name)
except AttributeError:
if name in self.data:
return self.data[name]
else:
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
在这个示例中,我们定义了一个 Example 类,它有一个 data 属性,其中包含一些数据。在 __getattribute__ 方法中,我们首先尝试使用 object.__getattribute__ 方法来获取属性的值。如果该属性存在,则直接返回它的值。如果该属性不存在,则抛出一个 AttributeError 异常,表示该属性不存在。在这种情况下,我们检查属性名称是否存在于 data 字典中。如果是,我们返回相应的值;否则,我们抛出一个 AttributeError 异常。
现在,我们可以创建一个 Example 实例,并访问它的属性:
>>> ex = Example()
>>> ex.a
1
>>> ex.b
2
>>> ex.c
3
>>> ex.d
AttributeError: 'Example' object has no attribute 'd'
在这个示例中,我们首先创建了一个 Example 实例,然后访问了它的属性 a、b 和 c,它们都存在于 data 字典中。然后我们试图访问属性 d,这个属性并不存在,于是 Python 调用了 __getattribute__ 方法,并抛出了一个 AttributeError 异常。
需要注意的是,__getattribute__ 方法可能会导致无限递归的问题。因为它处理所有属性的访问,如果在 __getattribute__ 方法中又访问了其他属性,那么就会再次调用 __getattribute__ 方法,从而导致递归调用。为了避免这个问题,通常在 __getattribute__ 方法中使用 object.__getattribute__ 方法来获取属性的值。
__setattr__魔术方法
__setattr__ 方法是 Python 中的一个特殊方法,用于在设置对象属性时被调用。当我们使用赋值语句或 setattr() 函数来设置一个对象的属性时,Python 会调用该对象的 __setattr__ 方法来进行赋值操作。该方法通常被用于实现属性的访问控制或修改属性赋值的行为。
以下是一个简单的示例,演示了如何使用 __setattr__ 方法:
class Example:
def __setattr__(self, name, value):
if name == 'value':
self.__dict__[name] = value
else:
raise AttributeError(f"cannot set attribute '{name}'")
ex = Example()
ex.value = 42
print(ex.value) # 输出 42
ex.other = 'other' # 抛出 AttributeError: cannot set attribute 'other'
在这个示例中,我们定义了一个 Example 类,它有一个 value 属性。在 __setattr__ 方法中,我们首先检查要设置的属性名称是否是 value。如果是,我们直接设置该属性的值。如果不是,我们抛出一个 AttributeError 异常,表示不能设置该属性。这样,我们就实现了对 value 属性的访问控制,只允许对它进行赋值操作。
现在,我们可以创建一个 Example 实例,并设置它的属性:
>>> ex = Example()
>>> ex.value = 42
>>> print(ex.value)
42
>>> ex.other = 'other'
AttributeError: cannot set attribute 'other'
在这个示例中,我们首先创建了一个 Example 实例,并设置了它的 value 属性。然后,我们试图设置一个 other 属性,它并不存在于 Example 类中,于是 Python 调用了 __setattr__ 方法,并抛出了一个 AttributeError 异常,表示不能设置该属性。
需要注意的是,__setattr__ 方法可能会导致无限递归的问题。因为它处理所有属性的设置,如果在 __setattr__ 方法中又设置了其他属性,那么就会再次调用 __setattr__ 方法,从而导致递归调用。为了避免这个问题,通常在 __setattr__ 方法中使用 self.__dict__[name] = value 来设置属性的值,而不是直接调用 super().__setattr__(name, value) 方法。
__delattr__魔术方法
__delattr__ 方法是 Python 中的一个特殊方法,用于在删除对象属性时被调用。当我们使用 del 关键字或 delattr() 函数来删除一个对象的属性时,Python 会调用该对象的 __delattr__ 方法来进行删除操作。
以下是一个简单的示例,演示了如何使用 __delattr__ 方法:
class Example:
def __init__(self):
self.value = 42
def __delattr__(self, name):
if name == 'value':
del self.__dict__[name]
else:
raise AttributeError(f"cannot delete attribute '{name}'")
ex = Example()
del ex.value
print(ex.__dict__) # 输出 {}
del ex.other # 抛出 AttributeError: cannot delete attribute 'other'
在这个示例中,我们定义了一个 Example 类,它有一个 value 属性。在 __delattr__ 方法中,我们首先检查要删除的属性名称是否是 value。如果是,我们直接删除该属性。如果不是,我们抛出一个 AttributeError 异常,表示不能删除该属性。这样,我们就实现了对 value 属性的访问控制,只允许删除它。
现在,我们可以创建一个 Example 实例,并删除它的属性:
>>> ex = Example()
>>> del ex.value
>>> print(ex.__dict__)
{}
>>> del ex.other
AttributeError: cannot delete attribute 'other'
在这个示例中,我们首先创建了一个 Example 实例,并删除了它的 value 属性。然后,我们试图删除一个 other 属性,它并不存在于 Example 类中,于是 Python 调用了 __delattr__ 方法,并抛出了一个 AttributeError 异常,表示不能删除该属性。
需要注意的是,__delattr__ 方法同样可能会导致无限递归的问题。因为它处理所有属性的删除,如果在 __delattr__ 方法中又删除了其他属性,那么就会再次调用 __delattr__ 方法,从而导致递归调用。为了避免这个问题,通常在 __delattr__ 方法中使用 del self.__dict__[name] 来删除属性,而不是直接调用 super().__delattr__(name) 方法。
__dir__魔术方法
__dir__() 是 Python 中的一个特殊方法,用于返回对象的属性列表。该方法返回的属性列表应该是对象的属性名称的迭代器,可以用于 Python 的 dir() 内置函数。
如果一个类没有定义 __dir__() 方法,Python 会尝试调用对象的 __dict__ 属性,并返回其中所有的属性名称和方法名称。这意味着在没有定义 __dir__() 方法的情况下,dir() 函数仍然能够返回一个对象的所有属性名称和方法名称。
以下是一个简单的示例,演示了如何使用 __dir__() 方法:
class Example:
def __init__(self):
self.value = 42
def __dir__(self):
return ['value', 'foo', 'bar']
ex = Example()
print(dir(ex)) # 输出 ['bar', 'foo', 'value']
在这个示例中,我们定义了一个 Example 类,它有一个 value 属性。在 __dir__() 方法中,我们返回了一个包含 'value'、'foo' 和 'bar' 三个字符串的列表。这意味着在调用 dir() 函数时,它将返回 ex 对象的 'value'、'foo' 和 'bar' 三个属性名称。
需要注意的是,__dir__() 方法应该返回一个迭代器,而不是一个列表。如果方法返回一个列表,那么在调用 dir() 函数时,Python 将在内部创建一个新的列表,从而可能导致性能问题。
如果需要使用默认的 __dir__() 行为,可以返回 None 或者调用 super().__dir__() 方法来获取默认的属性列表。例如:
class Example:
def __init__(self):
self.value = 42
def __dir__(self):
return None
ex = Example()
print(dir(ex)) # 输出 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'value']
在这个示例中,我们返回了 None,这意味着 __dir__() 方法不会影响 dir() 函数的行为。因此,我们得到了默认的属性列表,其中包括 Example 类和 ex 对象的所有属性和方法名称。
描述符相关的魔术方法
描述符(Descriptor)是 Python 中一种协议,用于控制属性的访问和修改行为。如果一个类的属性被赋予了描述符,那么每次对该属性进行访问或修改操作时,Python 都会自动调用描述符中相应的方法。
Python 中有三种描述符,分别是数据描述符、非数据描述符和属性。其中,数据描述符实现了 __get__()、__set__() 和 __delete__() 方法,可以控制属性的读写和删除操作;非数据描述符只实现了 __get__() 方法,仅控制属性的读操作;属性既可以是数据描述符也可以是非数据描述符,其默认实现是仅读取操作。
下面是一个简单的示例,演示了如何定义一个数据描述符:
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
def to_fahrenheit(self):
return (self._celsius * 1.8) + 32
def __get__(self, instance, owner):
print("Getting celsius value...")
return self._celsius
def __set__(self, instance, value):
print("Setting celsius value...")
if value < -273.15:
raise ValueError("Temperature too low")
self._celsius = value
def __delete__(self, instance):
print("Deleting celsius value...")
del self._celsius
celsius = Temperature()
temp = Temperature()
temp.celsius = 25 # 设置 celsius 属性的值
print(temp.celsius) # 输出 25
print(temp.to_fahrenheit()) # 输出 77.0
del temp.celsius # 删除 celsius 属性
print(hasattr(temp, "celsius")) # 输出 False
在这个示例中,我们定义了一个名为 Temperature 的类,并在其中实现了 __get__()、__set__() 和 __delete__() 方法。然后我们将 Temperature 类的实例作为 celsius 属性的值赋给了 temp 对象,并通过 temp.celsius 访问和修改 celsius 属性的值。每次访问和修改 celsius 属性时,Python 都会自动调用 __get__()、__set__() 和 __delete__() 方法。
需要注意的是,如果一个属性同时定义了 __get__()、__set__() 和__delete__() 方法,那么该属性就是一个数据描述符。如果一个属性只定义了 __get__() 方法,那么该属性就是一个非数据描述符。如果一个属性没有定义任何描述符方法,那么该属性就是一个普通属性。
描述符:https://docs.python.org/zh-cn/3/howto/descriptor.html
__get__魔术方法
__get__() 方法是 Python 中的一个特殊方法,用于定义一个描述符(Descriptor)。描述符是一种 Python 对象,它定义了另一个对象的访问方式,可以用于控制对该对象的访问、修改或者删除。
在 Python 中,描述符可以是一个实现了 __get__()、__set__() 和__delete__() 方法的对象。其中,__get__() 方法用于获取描述符所关联的属性的值。当一个属性被访问时,Python 会自动调用该属性所关联的描述符的 __get__() 方法,并返回描述符的返回值。
以下是一个简单的示例,演示了如何使用 __get__() 方法:
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def __get__(self, instance, owner):
return instance.temperature
def __set__(self, instance, value):
if value < -273.15:
raise ValueError("Temperature too low")
instance.temperature = value
class Temperature:
celsius = Celsius()
temp = Temperature()
temp.celsius = 25
print(temp.celsius) # 输出 25
print(temp.celsius.to_fahrenheit()) # 输出 77.0
在这个示例中,我们定义了一个 Celsius 类,它实现了一个描述符。描述符的 __get__() 方法返回 instance.temperature,因此在访问 Temperature 类的 celsius 属性时,它将返回 Celsius 实例的 temperature 属性的值。描述符的 __set__() 方法用于设置 Celsius 实例的 temperature 属性的值,并检查该值是否符合要求。
我们还定义了一个 Temperature 类,它包含一个 celsius 属性,该属性关联了 Celsius 描述符。当我们设置 temp.celsius 属性时,Python 会自动调用 Celsius 描述符的 __set__() 方法,并将 temp 对象作为 instance 参数传递给该方法。当我们获取 temp.celsius 属性时,Python 会自动调用 Celsius 描述符的 __get__() 方法,并返回描述符的返回值。
需要注意的是,描述符可以是类级别的或实例级别的。在上面的示例中,我们定义了一个类级别的描述符,因为它是一个类属性。如果将描述符定义为实例属性,则该描述符只与一个特定的实例对象相关联。
__set__魔术方法
__set__() 方法是 Python 中的一个特殊方法,用于定义描述符(Descriptor)对象的设置操作。描述符是一种 Python 对象,它定义了另一个对象的访问方式,可以用于控制对该对象的访问、修改或者删除。
在 Python 中,描述符可以是一个实现了 __get__()、__set__() 和__delete__() 方法的对象。其中,__set__() 方法用于设置描述符所关联的属性的值。当一个属性被设置时,Python 会自动调用该属性所关联的描述符的 __set__() 方法,并将被设置的值作为参数传递给该方法。
以下是一个简单的示例,演示了如何使用 __set__() 方法:
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def __get__(self, instance, owner):
return instance.temperature
def __set__(self, instance, value):
if value < -273.15:
raise ValueError("Temperature too low")
instance.temperature = value
class Temperature:
celsius = Celsius()
temp = Temperature()
temp.celsius = 25 # 设置 celsius 属性的值
print(temp.celsius) # 输出 25
print(temp.celsius.to_fahrenheit()) # 输出 77.0
在这个示例中,我们定义了一个 Celsius 类,它实现了一个描述符。描述符的 __get__() 方法返回 instance.temperature,因此在访问 Temperature 类的 celsius 属性时,它将返回 Celsius 实例的 temperature 属性的值。描述符的 __set__() 方法用于设置 Celsius 实例的 temperature 属性的值,并检查该值是否符合要求。
我们还定义了一个 Temperature 类,它包含一个 celsius 属性,该属性关联了 Celsius 描述符。当我们设置 temp.celsius 属性时,Python 会自动调用 Celsius 描述符的 __set__() 方法,并将 temp 对象作为 instance 参数传递给该方法,将 25 作为 value 参数传递给该方法。
需要注意的是,描述符可以是类级别的或实例级别的。在上面的示例中,我们定义了一个类级别的描述符,因为它是一个类属性。如果将描述符定义为实例属性,则该描述符只与一个特定的实例对象相关联。
__delete__魔术方法
__delete__() 方法是 Python 中的一个特殊方法,用于定义描述符(Descriptor)对象的删除操作。描述符是一种 Python 对象,它定义了另一个对象的访问方式,可以用于控制对该对象的访问、修改或者删除。
在 Python 中,描述符可以是一个实现了 __get__()、__set__() 和__delete__() 方法的对象。其中,__delete__() 方法用于删除描述符所关联的属性。当一个属性被删除时,Python 会自动调用该属性所关联的描述符的 __delete__() 方法。
以下是一个简单的示例,演示了如何使用 __delete__() 方法:
class Celsius:
def __init__(self, temperature=0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def __get__(self, instance, owner):
return instance.temperature
def __set__(self, instance, value):
if value < -273.15:
raise ValueError("Temperature too low")
instance.temperature = value
def __delete__(self, instance):
del instance.temperature
class Temperature:
celsius = Celsius()
temp = Temperature()
temp.celsius = 25 # 设置 celsius 属性的值
print(temp.celsius) # 输出 25
print(temp.celsius.to_fahrenheit()) # 输出 77.0
del temp.celsius # 删除 celsius 属性
print(hasattr(temp, "celsius")) # 输出 False
在这个示例中,我们添加了一个 __delete__() 方法到 Celsius 描述符中。当我们使用 del temp.celsius 删除 celsius 属性时,Python 会自动调用 Celsius 描述符的 __delete__() 方法,并将 temp 对象作为 instance 参数传递给该方法。
需要注意的是,如果一个描述符不支持删除操作,那么该描述符的 __delete__() 方法可以省略不写。
__slot__特殊方法
Python 中的 __slots__ 方法是一种类变量,它可以用来限制类实例的属性。当一个类定义了 __slots__ 变量时,Python 解释器就会为该类实例分配一个固定大小的数组来保存其属性,从而避免了使用字典来存储属性所带来的额外开销。
使用 __slots__ 变量的主要优点是可以提高实例访问的速度和减少内存消耗。因为 __slots__ 变量指定了实例可以拥有的属性,所以在访问实例属性时,Python 解释器不需要查找字典,而是可以直接通过数组下标来访问属性。此外,由于 __slots__ 变量限制了实例的属性,所以可以避免实例因为拥有过多的属性而消耗过多的内存。
下面是一个使用 __slots__ 变量的例子:
class Person:
__slots__ = ('name', 'age')
def __init__(self, name, age):
self.name = name
self.age = age
在上面的代码中,Person 类使用 __slots__ 变量限制了实例只能有 name 和 age 两个属性。由于 __slots__ 变量的存在,当创建 Person 类的实例时,实例不再拥有一个字典用于存储属性,而是使用一个固定大小的数组来存储属性,从而减少了内存消耗。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!