一篇文章让你彻底明白__getattr__、__getattribute__、__g...

在 Python 中,`__getattr__`、`__getattribute__`、`__setattr__`、`__delattr__` 等方法被称为属性访问的魔法方法,它们提供了自定义属性访问行为的能力。本文将详细介绍 `__getattr__`、`__getattribute__`、`__setattr__`、`__delattr__` 方法的不同之处,并提供一些典型案例来帮助你更好地理解它们的用法。

## `__getattr__` 方法

`__getattr__` 方法是在访问一个不存在的属性时被调用的。例如,我们定义了一个类 `Person`,但没有定义 `age` 属性:

```python

class Person:

def __init__(self, name):

self.name = name

```

如果我们在实例化一个 `Person` 对象并访问其 `age` 属性,就会触发 `__getattr__` 方法的调用:

```python

p = Person("Tom")

print(p.age) # 调用 __getattr__ 方法

```

我们可以在 `__getattr__` 方法中定义如何处理不存在的属性。例如,下面的 `__getattr__` 方法会在访问 `age` 属性时返回 18:

```python

class Person:

def __init__(self, name):

self.name = name

def __getattr__(self, name):

if name == "age":

return 18

raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

```

在这个例子中,`__getattr__` 方法首先判断是否访问的属性为 `age`,如果是,则返回 18。如果不是,则抛出 `AttributeError` 异常。

## `__getattribute__` 方法

`__getattribute__` 方法是在访问一个属性时被调用的,无论该属性是否存在。例如,我们定义了一个类 `Person2`:

```python

class Person2:

def __init__(self, name, age):

self.name = name

self.age = age

```

如果我们在实例化一个 `Person2` 对象并访问其 `name` 属性,就会触发 `__getattribute__` 方法的调用:

```python

p2 = Person2("Tom", 18)

print(p2.name) # 调用 __getattribute__ 方法

```

我们可以在 `__getattribute__` 方法中定义如何处理属性的访问。下面的 `__getattribute__` 方法会在访问任何属性时输出一条消息:

```python

class Person2:

def __init__(self, name, age):

self.name = name

self.age = age

def __getattribute__(self, name):

print(f"Accessing attribute {name}")

return super().__getattribute__(name)

```

在这个例子中,`__getattribute__` 方法被定义为在访问任何属性时输出一条消息。`super().__getattribute__(name)` 返回一个属性的值,如果属性不存在,则会触发 `AttributeError` 异常。

需要注意的是,在 `__getattribute__` 方法中调用 `self.name` 会再次触发 `__getattribute__` 方法的调用,进入了无限递归的状态。为了避免这种情况,可以使用 `super().__getattribute__(name)` 或 `object.__getattribute__(self, name)` 获取属性的值。

## `__setattr__` 方法

`__setattr__` 方法是在设置一个属性的值时被调用的。例如,我们定义了一个类 `Person3`:

```python

class Person3:

def __init__(self, name, age):

self.name = name

self.age = age

```

如果我们在实例化一个 `Person3` 对象并设置其 `age` 属性的值,就会触发 `__setattr__` 方法的调用:

```python

p3 = Person3("Tom", 18)

p3.age = 20 # 调用 __setattr__ 方法

```

我们可以在 `__setattr__` 方法中定义如何处理属性的设置。下面的 `__setattr__` 方法会在设置属性的值时输出一条消息:

```python

class Person3:

def __init__(self, name, age):

self.name = name

self.age = age

def __setattr__(self, name, value):

print(f"Setting attribute {name} to {value}")

super().__setattr__(name, value)

```

在这个例子中,`__setattr__` 方法被定义为在设置属性的值时输出一条消息。`super().__setattr__(name, value)` 设置属性的值。

需要注意的是,在 `__setattr__` 方法中调用 `self.name = value` 会再次触发 `__setattr__` 方法的调用,进入了无限递归的状态。为了避免这种情况,可以使用 `super().__setattr__(name, value)` 或 `object.__setattr__(self, name, value)` 设置属性的值。

## `__delattr__` 方法

`__delattr__` 方法是在删除一个属性时被调用的。例如,我们定义了一个类 `Person4`:

```python

class Person4:

def __init__(self, name, age):

self.name = name

self.age = age

```

如果我们在实例化一个 `Person4` 对象并删除其 `age` 属性,就会触发 `__delattr__` 方法的调用:

```python

p4 = Person4("Tom", 18)

del p4.age # 调用 __delattr__ 方法

```

我们可以在 `__delattr__` 方法中定义如何处理属性的删除。下面的 `__delattr__` 方法会在删除属性时输出一条消息:

```python

class Person4:

def __init__(self, name, age):

self.name = name

self.age = age

def __delattr__(self, name):

print(f"Deleting attribute {name}")

super().__delattr__(name)

```

在这个例子中,`__delattr__` 方法被定义为在删除属性时输出一条消息。`super().__delattr__(name)` 删除属性。

需要注意的是,在 `__delattr__` 方法中调用 `del self.name` 会再次触发 `__delattr__` 方法的调用,进入了无限递归的状态。为了避免这种情况,可以使用 `super().__delattr__(name)` 或 `object.__delattr__(self, name)` 删除属性。

## 案例说明

### 案例1:动态设置属性

下面的 `DynamicAttr` 类可以在访问一个不存在的属性时动态地创建它:

```python

class DynamicAttr:

def __getattr__(self, name):

if name.startswith("attr_"):

return "dynamic attribute"

raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

```

在这个例子中,`__getattr__` 方法首先判断是否访问的属性以 `attr_` 开头,如果是,则返回 "dynamic attribute"。如果不是,则抛出 `AttributeError` 异常。

下面是使用 `DynamicAttr` 类的示例:

```python

obj = DynamicAttr()

print(obj.attr_1) # dynamic attribute

```

在访问 `obj.attr_1` 属性时,由于该属性不存在,因此会调用 `__getattr__` 方法创建该属性。

### 案例2:属性访问计数器

下面的 `AttrCounter` 类可以统计访问类中所有属性的次数:

```python

class AttrCounter:

_count = 0

def __init__(self):

self.__dict__["_count"] = 0

def __getattribute__(self, name):

value = super().__getattribute__(name)

if isinstance(value, property):

return value.fget(self)

self.__dict__["_count"] += 1

return value

def __setattr__(self, name, value):

self.__dict__["_count"] += 1

super().__setattr__(name, value)

def __delattr__(self, name):

self.__dict__["_count"] += 1

super().__delattr__(name)

@property

def count(self):

return self._count

```

在这个例子中,`__getattribute__`、`__setattr__` 和 `__delattr__` 方法被用来统计访问、设置和删除属性的操作次数。`@property` 装饰器用来定义一个 `count` 属性,用于返回访问、设置和删除属性的操作次数。

下面是使用 `AttrCounter` 类的示例:

```python

class Person5(AttrCounter):

def __init__(self, name, age):

super().__init__()

self.name = name

self.age = age

p5 = Person5("Tom", 18)

print(p5.name, p5.age)

p5.name = "Jerry"

p5.age = 20

del p5.age

print(p5.count) # 4

```

在这个例子中,`Person5` 类继承自 `AttrCounter` 类,并自动统计了属性的访问、设置和删除操作次数。

## 总结

`__getattr__`、`__getattribute__`、`__setattr__`、`__delattr__` 方法提供了自定义属性访问行为的能力。`__getattr__` 方法在访问不存在的属性时被调用,`__getattribute__` 方法在访问属性时被调用,`__setattr__` 方法在设置属性值时被调用,`__delattr__` 方法在删除属性时被调用。在实际应用中,这些方法可以用于实现动态属性、属性计数器、属性验证、属性访问控制等功能。 如果你喜欢我们三七知识分享网站的文章, 欢迎您分享或收藏知识分享网站文章 欢迎您到我们的网站逛逛喔!https://www.37seo.cn/

点赞(6) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
发表
评论
返回
顶部