1
0

Python:面对对象进阶

This commit is contained in:
周中平 2023-08-10 17:38:36 +08:00
parent afdb4db9bb
commit 051df7c287
Signed by: zhouzhongping
GPG Key ID: 6666822800008000
2 changed files with 701 additions and 451 deletions

View File

@ -186,7 +186,7 @@ class Dog:
### Python 中实现继承 ### Python 中实现继承
#### 创建子类 #### 单继承
在 Python 中,我们可以通过在类定义时的括号中写入父类的名字来创建子类。新定义的类将会继承父类的所有属性和方法。 在 Python 中,我们可以通过在类定义时的括号中写入父类的名字来创建子类。新定义的类将会继承父类的所有属性和方法。
@ -200,6 +200,27 @@ class Dog(Animal):
pass pass
``` ```
#### 多继承
Python 支持多继承,意味着一个类可以继承多个父类。但这可能会导致一些复杂性,如“菱形问题”。
```python
class Swimming:
def swim(self):
return "Swims!"
class Flying:
def fly(self):
return "Flies!"
class Duck(Swimming, Flying):
pass
duck = Duck()
print(duck.swim()) # Outputs: Swims!
print(duck.fly()) # Outputs: Flies!
```
#### `super` 函数 #### `super` 函数
在 Python 中,`super`函数是一个内置函数,它可以用来调用父类的方法。这在你需要在子类中扩展父类的方法时特别有用。 在 Python 中,`super`函数是一个内置函数,它可以用来调用父类的方法。这在你需要在子类中扩展父类的方法时特别有用。
@ -226,6 +247,7 @@ class Dog(Animal):
1. **提高代码的灵活性**:多态能够让我们以更一般的方式编写代码,处理更广泛的数据类型,而不仅仅是特定的单一数据类型。这使得我们的代码更加灵活和可维护。 1. **提高代码的灵活性**:多态能够让我们以更一般的方式编写代码,处理更广泛的数据类型,而不仅仅是特定的单一数据类型。这使得我们的代码更加灵活和可维护。
2. **提高代码的可扩展性**:如果我们想要添加新的数据类型,我们只需要确保它们遵循现有的接口规定。这使得我们的代码更易于扩展和改进,而无需修改大量现有代码。 2. **提高代码的可扩展性**:如果我们想要添加新的数据类型,我们只需要确保它们遵循现有的接口规定。这使得我们的代码更易于扩展和改进,而无需修改大量现有代码。
3. **促进了类之间的解耦**:多态允许系统在不修改现有代码的情况下,添加新的对象类型。
### Python 中实现多态 ### Python 中实现多态
@ -259,315 +281,6 @@ print(animal_sound(Dog())) # 输出"Woof!"
print(animal_sound(Animal())) # 输出"Generic animal sound" print(animal_sound(Animal())) # 输出"Generic animal sound"
``` ```
## 特殊方法
### Python 的特殊方法
Python 的特殊方法是 Python 类中的一种特殊的方法,它们有固定的命名规则,即前后都有两个下划线(`__`)。这些方法在特定的情况下会被 Python 自动调用,因此它们有时也被称为魔术方法或者双下划线方法。特殊方法让我们可以自定义对象在特定情况下的行为,例如在进行算术运算,比较,迭代等操作时。
| 特殊方法 | 描述 |
| -------------------------------- | ---------------------------------------------------- |
| `__init__(self, ...)` | 构造函数,在创建新实例时调用。 |
| `__del__(self)` | 析构函数,在实例被销毁时调用。 |
| `__repr__(self)` | 定义该类的“官方”字符串表示。通常可以被`eval()`执行。 |
| `__str__(self)` | 定义该类的字符串表示,例如用于`print()`。 |
| `__bytes__(self)` | 定义`bytes()`的返回值。 |
| `__format__(self, format_spec)` | 定义`format()`的行为。 |
| `__lt__(self, other)` | 定义小于符号的行为。 |
| `__le__(self, other)` | 定义小于等于符号的行为。 |
| `__eq__(self, other)` | 定义等于符号的行为。 |
| `__ne__(self, other)` | 定义不等于符号的行为。 |
| `__gt__(self, other)` | 定义大于符号的行为。 |
| `__ge__(self, other)` | 定义大于等于符号的行为。 |
| `__hash__(self)` | 定义`hash()`的行为。 |
| `__bool__(self)` | 定义`bool()`的返回值。定义`True`和`False`的行为。 |
| `__getattr__(self, name)` | 定义当用户试图获取一个不存在的属性时的行为。 |
| `__setattr__(self, name, value)` | 定义对实例属性的赋值行为。 |
| `__delattr__(self, name)` | 定义对实例属性的删除行为。 |
| `__getattribute__(self, name)` | 定义属性访问行为。 |
| `__getitem__(self, key)` | 定义使用索引访问元素的行为。 |
| `__setitem__(self, key, value)` | 定义使用索引设置元素的行为。 |
| `__delitem__(self, key)` | 定义使用索引删除元素的行为。 |
| `__iter__(self)` | 定义迭代器行为,返回一个新的迭代器对象。 |
| `__reversed__(self)` | 定义`reversed()`返回值。定义逆序迭代的行为。 |
| `__len__(self)` | 定义`len()`返回值。定义对象包含元素的个数。 |
| `__add__(self, other)` | 定义加法的行为。 |
| `__sub__(self, other)` | 定义减法的行为。 |
| `__mul__(self, other)` | 定义乘法的行为。 |
| `__truediv__(self, other)` | 定义真除法的行为。 |
| `__floordiv__(self, other)` | 定义整除法的行为。 |
| `__mod__(self, other)` | 定义求模运算的行为。 |
| `__pow__(self, other[, modulo])` | 定义指数运算的行为。 |
| `__and__(self, other)` | 定义按位与运算的行为。 |
| `__xor__(self, other)` | 定义按位异或运算的行为。 |
| `__or__(self, other)` | 定义按位或运算的行为。 |
#### `__init__`
`__init__`方法是类的构造函数,当我们创建类的实例时,`__init__`方法会被自动调用。我们可以在`__init__`方法中初始化实例的属性。
```python
class MyClass:
def __init__(self, value):
self.value = value # 初始化实例属性
# 创建实例
mc = MyClass(10)
print(mc.value) # 输出10
```
#### `__del__`
`__del__`方法是类的析构函数,当一个实例被销毁时(例如被垃圾回收器回收时),`__del__`方法会被自动调用。注意,我们通常不需要在`__del__`方法中做清理工作Python的垃圾回收器会自动清理对象的资源。
```python
class MyClass:
def __del__(self):
print("Instance is being destroyed.")
mc = MyClass() # 创建实例
del mc # 销毁实例
```
#### `__repr__`
`__repr__`方法返回一个表示该对象的官方字符串,这个字符串通常可以被`eval()`执行来重新得到这个对象。如果我们没有定义`__str__`方法,那么在调用`str()`或`print()`时也会使用`__repr__`的返回值。
```python
class MyClass:
def __repr__(self):
return "MyClass()"
mc = MyClass() # 创建实例
print(mc) # 输出MyClass()
```
#### `__str__`
`__str__`方法返回一个表示该对象的字符串,这个字符串通常用于给用户看。当我们调用`str()`或`print()`时,会使用`__str__`的返回值。
```python
class MyClass:
def __str__(self):
return "This is a MyClass instance."
mc = MyClass() # 创建实例
print(mc) # 输出This is a MyClass instance.
```
*注意,`__repr__`和`__str__`的区别在于,**`__repr__`更侧重于开发,而`__str__`更侧重于用户。***
#### `__bytes__`
`__bytes__`方法定义了当我们调用`bytes()`时的行为。它应该返回一个字节串。
```python
class MyClass:
def __bytes__(self):
return b'MyClass instance'
mc = MyClass() # 创建实例
print(bytes(mc)) # 输出b'MyClass instance'
```
#### `__format__`
`__format__`方法定义了当我们调用`format()`或使用格式化字符串f-string时的行为。`format_spec`是一个格式说明符,它是在格式化字符串中`:`后面的部分。
```python
class MyClass:
def __format__(self, format_spec):
if format_spec == 'fancy':
return 'This is a fancy MyClass instance.'
return 'This is a MyClass instance.'
mc = MyClass() # 创建实例
print(f"{mc:fancy}") # 输出This is a fancy MyClass instance.
```
#### `__hash__`
`__hash__`方法定义了当我们调用`hash()`时的行为。它应该返回一个整数,这个整数会被用于在字典等哈希表中快速比较键。
```python
class MyClass:
def __init__(self, value):
self.value = value
def __hash__(self):
return hash(self.value)
mc = MyClass(10) # 创建实例
print(hash(mc)) # 输出10
```
#### `__bool__`
`__bool__`方法定义了当我们调用`bool()`时的行为。它应该返回`True`或`False`。
```python
class MyClass:
def __init__(self, value):
self.value = value
def __bool__(self):
return bool(self.value)
print(bool(MyClass(0))) # 输出False
print(bool(MyClass(1))) # 输出True
```
#### `__getattr__`
`__getattr__`方法定义了当我们试图获取一个不存在的属性时的行为。`name`是我们试图获取的属性的名称。
```python
class MyClass:
def __getattr__(self, name):
return f"{name} does not exist."
mc = MyClass() # 创建实例
print(mc.unknown_attr) # 输出unknown_attr does not exist.
```
#### `__setattr__`
`__setattr__`方法定义了对实例属性的赋值行为。`name`是属性的名称,`value`是我们试图赋给属性的值。
```python
class MyClass:
def __setattr__(self, name, value):
self.__dict__[name] = value # 在__dict__中设置属性
print(f"Set {name} to {value}.")
mc = MyClass() # 创建实例
mc.attr = 10 # 输出Set attr to 10.
```
注意,为了防止在`__setattr__`中赋值属性时再次调用`__setattr__`,导致无限递归,我们需要直接在实例的`__dict__`属性中设置属性。
#### `__delattr__`
`__delattr__`方法定义了对实例属性的删除行为。`name`是我们试图删除的属性的名称。
```python
class MyClass:
attr = 10
def __delattr__(self, name):
del self.__dict__[name] # 在__dict__中删除属性
print(f"Deleted {name}.")
mc = MyClass() # 创建实例
del mc.attr # 输出Deleted attr.
```
注意,为了防止在`__delattr__`中删除属性时再次调用`__delattr__`,导致无限递归,我们需要直接在实例的`__dict__`属性中删除属性。
#### `__getattribute__`
`__getattribute__`方法定义了属性访问行为。无论属性是否存在,只要我们试图访问属性,就会调用`__getattribute__`。
```python
class MyClass:
def __getattribute__(self, name):
return f"You are trying to access {name}."
mc = MyClass() # 创建实例
print(mc.attr) # 输出You are trying to access attr.
```
注意,如果我们定义了`__getattribute__`方法,那么`__getattr__`就不会被调用。因为无论属性是否存在,`__getattribute__`都会被调用。
#### `__getitem__`
`__getitem__`方法定义了使用索引访问元素的行为。`key`是索引。
```python
class MyClass:
def __getitem__(self, key):
return f"You are trying to access key {key}."
mc = MyClass() # 创建实例
print(mc[10]) # 输出You are trying to access key 10.
```
#### `__setitem__`
`__setitem__`方法定义了使用索引设置元素的行为。`key`是索引,`value`是我们试图设置的值。
```python
class MyClass:
def __setitem__(self, key, value):
print(f"Set key {key} to {value}.")
mc = MyClass() # 创建实例
mc[10] = "value" # 输出Set key 10 to value.
```
#### `__delitem__`
`__delitem__`方法定义了使用索引删除元素的行为。`key`是索引。
```python
class MyClass:
def __delitem__(self, key):
print(f"Deleted key {key}.")
mc = MyClass() # 创建实例
del mc[10] # 输出Deleted key 10.
```
#### `__iter__`
`__iter__`方法定义了迭代器行为,它应该返回一个新的迭代器对象。
```python
class MyClass:
def __init__(self):
self.data = [1, 2, 3]
def __iter__(self):
return iter(self.data)
mc = MyClass() # 创建实例
for i in mc: # 输出1 2 3
print(i)
```
#### `__reversed__`
`__reversed__`方法定义了`reversed()`的返回值。它应该返回一个新的反向迭代器对象。
```python
class MyClass:
def __init__(self):
self.data = [1, 2, 3]
def __reversed__(self):
return reversed(self.data)
mc = MyClass() # 创建实例
for i in reversed(mc): # 输出3 2 1
print(i)
```
#### `__len__`
`__len__`方法定义了`len()`的返回值。它应该返回一个整数,表示对象包含的元素的个数。
```python
class MyClass:
def __init__(self):
self.data = [1, 2, 3]
def __len__(self):
return len(self.data)
mc = MyClass() # 创建实例
print(len(mc)) # 输出3
```
## 抽象类和接口 ## 抽象类和接口
### 抽象类和接口的概念 ### 抽象类和接口的概念
@ -576,7 +289,7 @@ print(len(mc)) # 输出3
接口是一种特殊的抽象类,它只定义了一组方法的签名,没有提供任何实现。接口定义了一组行为,任何实现了这些行为的类都可以说是实现了这个接口。 接口是一种特殊的抽象类,它只定义了一组方法的签名,没有提供任何实现。接口定义了一组行为,任何实现了这些行为的类都可以说是实现了这个接口。
### Python 的abc模块 ### Python 的 abc 模块
在 Python 中,我们使用 abc 模块来创建抽象类和接口。abc 模块提供了`ABC`基类和`abstractmethod`装饰器,我们可以使用它们来定义抽象类和抽象方法。 在 Python 中,我们使用 abc 模块来创建抽象类和接口。abc 模块提供了`ABC`基类和`abstractmethod`装饰器,我们可以使用它们来定义抽象类和抽象方法。

View File

@ -7,189 +7,726 @@ tags:
- 进阶 - 进阶
sidebar_position: 1 sidebar_position: 1
author: 7Wate author: 7Wate
date: 2023-01-16 date: 2023-08-10
--- ---
### slots 魔法 ## 生命周期
如果我们需要限定自定义类型的对象只能绑定某些属性可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效对子类并不起任何作用。 ```mermaid
graph TD
A[类定义阶段: class MyClass] --> B[类对象创建]
B --> C1[类属性访问: MyClass.class_attr]
B --> C[实例化阶段: __new__ method]
C --> D[初始化阶段: __init__ method]
D --> E1[属性的设置与获取: __setattr__, __getattr__]
D --> E2[对象的表示: __str__, __repr__]
D --> E3[方法调用]
D --> E4[上下文管理: __enter__, __exit__]
D --> E5[对象比较: __eq__, __lt__]
D --> E6[对象的计算: __add__, __mul__]
E1 --> F
E2 --> F
E3 --> F
E4 --> F
E5 --> F
E6 --> F
F[对象销毁: del obj or GC] --> G[__del__ method]
G --> H[垃圾回收: Cleanup by Python GC]
```
1. **类定义阶段**:在这个阶段定义类的方法和属性。
2. **类对象创建**:当 Python 读取到类定义时,它会创建一个类对象。
3. **类属性访问**:在实例化之前,可以访问类的静态和类属性。
4. **实例化阶段**:使用类名创建对象时,首先调用`__new__`方法来分配内存。
5. **初始化阶段**:紧随其后,`__init__`方法会被调用,对新对象进行初始化。
6. **属性的设置与获取**:在对象的生命周期中,你可以使用`__getattr__`, `__setattr__`, `__getattribute__`等特殊方法来控制属性的访问。
7. **对象的表示**:当尝试显示对象时(例如,通过 print`__str__`和`__repr__`方法可以被调用。
8. **方法调用**:可以调用对象的方法。
9. **上下文管理**:使用`with`语句时,对象的`__enter__`和`__exit__`方法会被调用。
10. **对象比较**:使用比较操作符时(如==或>`__eq__`, `__lt__`, `__le__`等特殊方法会被调用。
11. **对象的计算**:如加法或乘法等运算符,会调用`__add__`, `__mul__`等特殊方法。
12. **对象销毁**:当对象的引用计数减少到零或者被明确销毁(使用`del`语句)时,`__del__`方法会被调用。
13. **垃圾回收**Python 的垃圾回收器会识别循环引用,并在适当的时候销毁相关对象。
## 特殊方法
Python 中的类包含了许多特殊方法,它们经常被称为**魔术方法magic methods**或者**双下方法dunder methods**,因为它们的名字都是以双下划线开始和结束的。这些特殊方法为 Python 的对象提供了许多内置的功能,如算术运算、迭代和字符串表示。
魔术方法是 Python 类的特殊方法,它们定义了许多 Python 中基本的操作。例如,当你为一个对象定义了 `__add__` 方法时,这个对象就可以使用 `+` 运算符。这些方法的名称都是以两个下划线开始和结束,这也是为什么它们被称为双下方法的原因。
### 基础方法
#### `__init__`
`__init__`方法是类的构造函数,当我们创建类的实例时,`__init__`方法会被自动调用。我们可以在`__init__`方法中初始化实例的属性。
```python ```python
class Person(object): class MyClass:
def __init__(self, value):
self.value = value # 初始化实例属性
# 限定Person对象只能绑定_name, _age和_gender属性 # 创建实例
__slots__ = ('_name', '_age', '_gender') mc = MyClass(10)
print(mc.value) # 输出10
```
def __init__(self, name, age): #### `__del__`
self._name = name
self._age = age
@property `__del__`方法是类的析构函数,当一个实例被销毁时(例如被垃圾回收器回收时),`__del__`方法会被自动调用。注意,我们**通常不需要在`__del__`方法中做清理工作Python的垃圾回收器会自动清理对象的资源。**
def name(self):
return self._name
@property ```python
def age(self): class MyClass:
return self._age def __del__(self):
print("Instance is being destroyed.")
@age.setter mc = MyClass() # 创建实例
def age(self, age): del mc # 销毁实例
self._age = age ```
def play(self): #### `__str__`
if self._age <= 16:
print('%s正在玩飞行棋.' % self._name) `__str__`方法返回一个表示该对象的字符串,这个字符串通常用于给用户看。当我们调用`str()`或`print()`时,会使用`__str__`的返回值。
```python
class MyClass:
def __str__(self):
return "This is a MyClass instance."
mc = MyClass() # 创建实例
print(mc) # 输出This is a MyClass instance.
```
*注意,`__repr__`和`__str__`的区别在于,**`__repr__`更侧重于开发,而`__str__`更侧重于用户。***
#### `__repr__`
`__repr__`方法返回一个表示该对象的官方字符串,这个字符串通常可以被`eval()`执行来重新得到这个对象。如果我们没有定义`__str__`方法,那么在调用`str()`或`print()`时也会使用`__repr__`的返回值。
```python
class MyClass:
def __repr__(self):
return "MyClass()"
mc = MyClass() # 创建实例
print(mc) # 输出MyClass()
```
#### `__format__`
`__format__`方法定义了当我们调用`format()`或使用格式化字符串f-string时的行为。`format_spec`是一个格式说明符,它是在格式化字符串中`:`后面的部分。
```python
class MyClass:
def __format__(self, format_spec):
if format_spec == 'fancy':
return 'This is a fancy MyClass instance.'
return 'This is a MyClass instance.'
mc = MyClass() # 创建实例
print(f"{mc:fancy}") # 输出This is a fancy MyClass instance.
```
### 数学运算
比较运算的魔术方法允许类的实例之间进行比较。例如,`__eq__`定义了对象的等于操作。这些方法的使用可以使您的类实例支持标准的比较操作符,如`==`, `!=`, `+`, `-`, `%`, 和 `@`。类似地,可以为其他数学运算符定义其他魔术方法。
```python
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __eq__(self, other):
if isinstance(other, Book):
return self.title == other.title and self.author == other.author
return False
# 比较两本书是否具有相同的标题和作者
book1 = Book('1984', 'George Orwell')
book2 = Book('1984', 'George Orwell')
book3 = Book('Brave New World', 'Aldous Huxley')
print(book1 == book2) # 输出True
print(book1 == book3) # 输出False
```
```python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented
# 使用 + 操作符组合两个向量
v1 = Vector(1, 2)
v2 = Vector(2, 3)
v3 = v1 + v2
print(v3.x, v3.y) # 输出3 5
```
| 运算符 | 对应的魔术方法 | 描述 |
| ------ | -------------- | ---------- |
| `==` | `__eq__` | 等于 |
| `!=` | `__ne__` | 不等于 |
| `<` | `__lt__` | 小于 |
| `<=` | `__le__` | 小于或等于 |
| `>` | `__gt__` | 大于 |
| `>=` | `__ge__` | 大于或等于 |
| `+` | `__add__` | 加法 |
| `-` | `__sub__` | 减法 |
| `*` | `__mul__` | 乘法 |
| `/` | `__truediv__` | 真除 |
| `//` | `__floordiv__` | 整除 |
| `%` | `__mod__` | 取模 |
| `**` | `__pow__` | 乘方 |
| `@` | `__matmul__` | Python 3.5+ 矩阵乘法 |
### 容器方法
#### `__len__`
`__len__`方法定义了`len()`的返回值。它应该返回一个整数,表示对象包含的元素的个数。
```python
class MyClass:
def __init__(self):
self.data = [1, 2, 3]
def __len__(self):
return len(self.data)
mc = MyClass() # 创建实例
print(len(mc)) # 输出3
```
#### `__getitem__`
`__getitem__`方法定义了使用索引访问元素的行为。`key`是索引。
```python
class MyClass:
def __getitem__(self, key):
return f"You are trying to access key {key}."
mc = MyClass() # 创建实例
print(mc[10]) # 输出You are trying to access key 10.
```
#### `__setitem__`
`__setitem__`方法定义了使用索引设置元素的行为。`key`是索引,`value`是我们试图设置的值。
```python
class MyClass:
def __setitem__(self, key, value):
print(f"Set key {key} to {value}.")
mc = MyClass() # 创建实例
mc[10] = "value" # 输出Set key 10 to value.
```
#### `__delitem__`
`__delitem__`方法定义了使用索引删除元素的行为。`key`是索引。
```python
class MyClass:
def __delitem__(self, key):
print(f"Deleted key {key}.")
mc = MyClass() # 创建实例
del mc[10] # 输出Deleted key 10.
```
#### `__iter__`、`__next__`
迭代器协议的魔术方法允许对象支持迭代,这意味着您可以在对象上使用`for`循环。为了使一个对象可迭代,您需要定义`__iter__`和`__next__`两个魔术方法。
- `__iter__`返回对象本身,它应该返回一个实现了`__next__`的迭代器对象。
- `__next__`方法返回序列中的下一个值。如果所有项都返回了,那么它应该引发一个`StopIteration`异常来通知迭代的完成。
```python
class Counter:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current < self.end:
value = self.current
self.current += 1
return value
else: else:
print('%s正在玩斗地主.' % self._name) raise StopIteration
# 使用Counter迭代器
counter = Counter(1, 4)
def main(): for number in counter:
person = Person('王大锤', 22) print(number)
person.play() # 输出:
person._gender = '男' # 1
# AttributeError: 'Person' object has no attribute '_is_gay' # 2
# person._is_gay = True # 3
``` ```
### @property 装饰器 在上述`Counter`类中,我们定义了一个简单的迭代器,它从`start`开始每次迭代增加1直到`end`为止。`for`循环通过调用`__iter__`来获取迭代器对象,并在每次迭代中调用`__next__`,直到捕获`StopIteration`异常为止。
如果想访问属性可以通过属性的 getter访问器和 setter修改器方法进行对应的操作。要做到这点就可以考虑使用@property包装器来包装getter和setter方法使得对属性的访问既安全又方便代码如下所示。 ### 属性访问
#### `__dir__`
`__dir__`方法返回类中定义的属性、方法等的列表。它对内置的`dir()`函数的行为进行重载。
```python ```python
class Person(object): class MyClass:
def __dir__(self):
return ["custom_attr1", "custom_attr2"]
def __init__(self, name, age): mc = MyClass()
self._name = name print(dir(mc)) # 输出:['custom_attr1', 'custom_attr2']
self._age = age ```
# 访问器 - getter方法 #### `__getattr__`
@property
def name(self):
return self._name
# 访问器 - getter方法 当尝试访问一个不存在的属性时,`__getattr__`方法会被调用。`name`是试图访问的属性名称。
@property
def age(self):
return self._age
# 修改器 - setter方法 ```python
@age.setter class MyClass:
def age(self, age): def __getattr__(self, name):
self._age = age return f"{name} does not exist."
def play(self): mc = MyClass() # 创建实例
if self._age <= 16: print(mc.unknown_attr) # 输出unknown_attr does not exist.
print('%s正在玩飞行棋.' % self._name) ```
*值得注意的是,**`__getattr__`只有在所请求的属性不存在时才会被调用。***
#### `__setattr__`
每当尝试设置一个属性值时,`__setattr__`都会被调用,不论该属性是否存在。`name`是试图设置的属性的名称,而`value`是试图赋给该属性的值。
```python
class MyClass:
def __setattr__(self, name, value):
self.__dict__[name] = value # 防止无限递归, 需要在__dict__中设置属性
print(f"Set {name} to {value}.")
mc = MyClass() # 创建实例
mc.attr = 10 # 输出Set attr to 10.
```
#### `__delattr__`
当试图删除一个属性时,`__delattr__`方法会被调用。`name`是试图删除的属性的名称。
```python
class MyClass:
attr = 10
def __delattr__(self, name):
del self.__dict__[name] # 防止无限递归, 需要在__dict__中删除属性
print(f"Deleted {name}.")
mc = MyClass() # 创建实例
del mc.attr # 输出Deleted attr.
```
#### `__getattribute__`
每次尝试访问一个属性时,`__getattribute__`方法都会被调用,无论该属性是否存在。
```python
class MyClass:
def __getattribute__(self, name):
return f"You are trying to access {name}."
mc = MyClass() # 创建实例
print(mc.attr) # 输出You are trying to access attr.
```
重要的是,**如果`__getattribute__`被定义,那么`__getattr__`不会被调用,因为`__getattribute__`的优先级更高。***
### 上下文管理
#### `__enter__`、`__exit__`
当使用`with`语句进入上下文管理时,`__enter__`方法会被调用。它应该返回上下文管理器对象本身或其他与上下文相关的对象。
当`with`语句块结束时,`__exit__`方法会被调用。它接收三个参数:`exc_type`、`exc_val`和`exc_tb`,分别代表异常类型、异常值和异常回溯。如果`with`语句块中没有发生异常,这三个参数的值都为`None`。
```python
class ContextManager:
def __enter__(self):
print("Entering the context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
print("Exiting the context without any exception.")
else: else:
print('%s正在玩斗地主.' % self._name) print(f"Exiting the context with exception: {exc_val}")
def say_hello(self):
print("Hello from inside the context!")
with ContextManager() as cm:
cm.say_hello()
def main(): # 输出:
person = Person('王大锤', 12) # Entering the context
person.play() # Hello from inside the context!
person.age = 22 # Exiting the context without any exception.
person.play()
# person.name = '白元芳' # AttributeError: can't set attribute
if __name__ == '__main__':
main()
``` ```
### @staticmethod 静态方法 如果在`with`语句块中引发了异常,`__exit__`方法可以选择处理这个异常(例如记录日志)并返回`True`来阻止异常向外传播,或者返回`False`(或`None`)让异常继续向外传播。
可以使用`@staticmethod`注解定义静态方法,通过直接调用类使用方法。 上下文管理器是一种非常强大的工具特别是当涉及到需要设置和清理资源的任务时例如文件I/O、网络连接或数据库连接。
### 描述符
描述符是实现了描述符协议的对象。描述符协议由`__get__`、`__set__`和`__delete__`方法组成。描述符用于创建那些需要特殊行为的对象属性,例如类型检查或只读属性。
#### `__get__`
`__get__`方法定义了在获取属性时应执行的行为。当试图获取属性值时,这个方法会被调用。
```python ```python
from math import sqrt class Descriptor:
def __get__(self, instance, owner):
print(f"Getting value from {instance.__class__.__name__}")
class MyClass:
attr = Descriptor()
class Triangle(object): mc = MyClass()
mc.attr
def __init__(self, a, b, c): # 输出:
self._a = a # Getting value from MyClass
self._b = b
self._c = c
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
def perimeter(self):
return self._a + self._b + self._c
def area(self):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) *
(half - self._b) * (half - self._c))
def main():
a, b, c = 3, 4, 5
# 静态方法和类方法都是通过给类发消息来调用的
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('无法构成三角形.')
if __name__ == '__main__':
main()
``` ```
### @classmethod 类方法 #### `__set__`
Python 还可以在类中定义类方法,类方法的第一个参数约定名为 cls它代表的是当前类相关的信息的对象类本身也是一个对象有的地方也称之为类的元数据对象通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象代码如下所示。 `__set__`方法定义了在设置属性值时应执行的行为。当试图给属性赋值时,这个方法会被调用。
```python ```python
from time import time, localtime, sleep class Descriptor:
def __set__(self, instance, value):
print(f"Setting value {value} to {instance.__class__.__name__}")
class MyClass:
attr = Descriptor()
class Clock(object): mc = MyClass()
"""数字时钟""" mc.attr = 10
# 输出:
# Setting value 10 to MyClass
```
def __init__(self, hour=0, minute=0, second=0): #### `__delete__`
self._hour = hour
self._minute = minute `__delete__`方法定义了当删除属性时应执行的行为。当试图删除属性时,这个方法会被调用。
self._second = second
```python
class Descriptor:
def __delete__(self, instance):
print(f"Deleting value from {instance.__class__.__name__}")
class MyClass:
attr = Descriptor()
del mc.attr
# 输出:
# Deleting value from MyClass
```
### 使对象可调用
**在 Python 中,函数是一类对象,可以调用它们。**但是,您知道您也可以使自己的对象表现得像函数一样吗?通过定义`__call__`魔术方法,您可以使类的实例表现得像函数,从而允许对它们进行调用。
#### `__call__`
当实例作为函数被“调用”时,`__call__`方法就会被执行。这提供了一种优雅的方式来使用对象,同时保持其对象性质。通过这种方式,您的对象不仅可以表示数据,还可以表现得像函数,这增加了编码的灵活性和创造性。
```python
class Greeter:
def __init__(self, greeting="Hello"):
self.greeting = greeting
def __call__(self, name):
return f"{self.greeting}, {name}!"
# 创建一个Greeter实例
hello_greeter = Greeter("Hello")
print(hello_greeter("John")) # 输出Hello, John!
bonjour_greeter = Greeter("Bonjour")
print(bonjour_greeter("Pierre")) # 输出Bonjour, Pierre!
```
### 值比较
#### `__hash__`
`__hash__`方法返回对象的哈希值。哈希值通常用于字典的键值和其他需要快速查找的数据结构中。如果一个对象是可变的,通常最好不要实现此方法。如果对象定义了`__eq__`方法并且是不可变的,则通常也应定义此方法。
```python
class MyClass:
def __init__(self, value):
self.value = value
def __eq__(self, other):
if isinstance(other, MyClass):
return self.value == other.value
return NotImplemented
def __hash__(self):
return hash(self.value)
obj1 = MyClass(5)
obj2 = MyClass(5)
my_dict = {obj1: "a"}
print(my_dict[obj2]) # 输出a
```
#### `__bool__`
`__bool__`方法用于实现`bool()`内置函数的调用。当我们调用`bool()`函数或使用对象在条件语句(例如`if obj:`)中作为条件时,会调用此方法。如果`__bool__`没有被定义,`__len__`会被调用(如果已定义)。如果两者都未定义,所有实例都默认为`True`。
```python
class MyClass:
def __init__(self, value):
self.value = value
def __bool__(self):
return bool(self.value)
obj1 = MyClass(0)
obj2 = MyClass(5)
print(bool(obj1)) # 输出False
print(bool(obj2)) # 输出True
```
### 类型转换
#### `__int__`
`__int__`方法允许将一个对象转换为整数。当使用`int()`内置函数时,如果对象实现了此方法,会被调用。
```python
class MyClass:
def __int__(self):
return 42
obj = MyClass()
print(int(obj)) # 输出42
```
#### `__float__`
`__float__`方法允许将一个对象转换为浮点数。当使用`float()`内置函数时,如果对象实现了此方法,会被调用。
```python
class MyClass:
def __float__(self):
return 42.0
obj = MyClass()
print(float(obj)) # 输出42.0
```
#### `__complex__`
`__complex__`方法允许将一个对象转换为复数。当使用`complex()`内置函数时,如果对象实现了此方法,会被调用。
```python
class MyClass:
def __complex__(self):
return 3 + 4j
obj = MyClass()
print(complex(obj)) # 输出:(3+4j)
```
#### `__bytes__`
`__bytes__`方法定义了当我们调用`bytes()`时的行为。它应该返回一个字节串。
```python
class MyClass:
def __bytes__(self):
return b'MyClass instance'
mc = MyClass() # 创建实例
print(bytes(mc)) # 输出b'MyClass instance'
```
## 类和静态方法
在 Python 中,类是一个创建对象的蓝图。对象则是基于类定义的实例。默认情况下,在类内部定义的方法是实例方法。
### 实例方法
实例方法的第一个参数是 `self`,代表类的实例对象。它可以访问和修改与实例相关的属性和方法。实例方法**只能由其实例对象调用**。
```python
class Example:
def instance_method(self):
return "This is an instance method", self
e = Example()
print(e.instance_method())
# 输出 ('This is an instance method', <__main__.Example object at 0x7f49af98f610>)
```
### 类方法
类方法使用 `@classmethod` 装饰器定义。其第一个参数是`cls`,代表类本身。类方法既可以由类直接调用,也可以被其实例调用。
```python
class Example:
class_var = "Class Variable"
@classmethod @classmethod
def now(cls): def class_method(cls):
ctime = localtime(time()) return "This is a class method accessing:", cls.class_var
return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
def run(self): print(Example.class_method())
"""走字""" # 输出 ('This is a class method accessing:', 'Class Variable')
self._second += 1 ```
if self._second == 60:
self._second = 0 ### 静态方法
self._minute += 1
if self._minute == 60: 静态方法使用 `@staticmethod` 装饰器定义。它不需要传递`self`或`cls`参数。**静态方法不能访问或修改类或实例的属性和方法。它仅仅与它所在的类相关**,但不需要访问类的特性。
self._minute = 0
self._hour += 1 ```python
if self._hour == 24: class Example:
self._hour = 0 @staticmethod
def static_method():
def show(self): return "This is a static method"
"""显示时间"""
return '%02d:%02d:%02d' % \ print(Example.static_method())
(self._hour, self._minute, self._second) # 输出 This is a static method
```
def main(): ### 类方法 vs 静态方法
# 通过类方法创建对象并获取系统时间
clock = Clock.now() | 类型 | 用途 | 优点 | 缺点 |
while True: | ------------ | ----------------------------------------------------- | ----------------------------- | ---------------------------- |
print(clock.show()) | **类方法** | 访问/修改类属性,方法继承于子类 | 可访问/修改类属性,适用于继承 | 不能访问实例特有的属性 |
sleep(1) | **静态方法** | 不需访问实例/类数据的操作,与类相关但不需访问类或实例 | 无需实例化,代码组织清晰 | 不能访问类和实例的属性或方法 |
clock.run()
在面向对象编程中,合理地使用实例方法、类方法和静态方法可以使代码更有组织性,更易于维护。
if __name__ == '__main__': ## 属性
main()
### `@property` 装饰器
在 Python 中,`@property` 装饰器使我们能够将类中的方法用作属性,从而实现对属性的控制。它可以用于确保属性的读取和设置遵循某种特定的逻辑。
```python
class Example:
def __init__(self, number):
self._number = number
@property
def number(self):
return self._number
e = Example(5)
print(e.number)
# 输出 5
```
### 使用 setter 和 getter 方法
Setter 和 Getter 在 Python 中用于控制属性的访问和赋值。
#### `@<property_name>.setter`
在定义了属性的 getter 方法后,我们可以使用 `@<property_name>.setter` 装饰器定义相应的 setter 方法,以控制该属性的赋值逻辑。
```python
class Example:
def __init__(self, number):
self._number = number
@property
def number(self):
return self._number
@number.setter
def number(self, value):
self._number = value
e = Example(5)
e.number = 10
print(e.number)
# 输出 10
```
#### `@<property_name>.deleter`
除了设置和获取属性,我们还可以定义如何删除属性。
```python
class Example:
def __init__(self, number):
self._number = number
@property
def number(self):
return self._number
@number.deleter
def number(self):
print("Deleting number")
del self._number
e = Example(5)
del e.number
```
### 属性保护
在Python中我们通常**使用下划线来表示属性应该是私有的或受保护的。**
- **受保护的属性**: 通常使用单下划线前缀`_`来定义,例如`_name`。这只是一个约定,表示这个属性是为类内部使用的,但外部仍然可以访问。
- **私有属性**: 使用双下划线前缀`__`来定义,例如`__name`。Python会对其进行名称修饰使得在类的外部更难直接访问。
```python
class Example:
def __init__(self):
self.__private_attr = "I am private"
self._protected_attr = "I am protected"
def reveal(self):
print(self.__private_attr)
e = Example()
print(e._protected_attr)
# print(e.__private_attr) # 将会抛出错误异常
e.reveal() # 正常工作
```
#### `__slots__` 限制动态属性的添加
为了提高性能和内存效率Python允许在类定义中使用`__slots__`来限制可以添加到对象的属性。这通常在需要创建大量对象时很有用。
```python
class Example:
__slots__ = ['name', 'age']
e = Example()
e.name = "ChatGPT"
# e.salary = 10000 # 将会抛出错误异常
``` ```