PYTHON-Class
面向对象
类与实例的数据,都保存在一个名为 dict 的字典属性中
灵活利用 dict 属性,能帮你做到常规做法难以完成的一些事情
使用 @classmethod 可以定义类方法,类方法常用作工厂方法
使用 @staticmethod 可以定义静态方法,静态方法不依赖实例状态,是一种无状态方法
使用 @property 可以定义动态属性对象,该属性对象的获取、设置和删除行为都支持自定义
@property
无描述符时,实现属性校验功能
在下面的代码里,我实现了一个 Person 类:
1 | class Person: |
class Person:
…
@property
def age(self):
return self._age
@age.setter
def age(self, value):
"""设置年龄,只允许 0~150 之间的数值"""
try:
value = int(value)
except (TypeError, ValueError):
raise ValueError('value is not a valid integer!')
if not (0 < value < 150):
raise ValueError('value must between 0 and 150!')
self._age = value
1 | 通过在 age 属性的 setter 方法里增加校验,我最终实现了想要的效果: |
p = Person(‘piglei’, ‘invalid_age’) ➊
…
ValueError: value is not a valid integer!
p = Person(‘piglei’, ‘200’) ➋
…
ValueError: value must between 0 and 150!
p = Person(‘piglei’, 18) ➌
p.age
18
1 |
|
class Rectangle:
@property
def width(self): ...
@width.setter
def width(self): ...
@property
def height(self): ...
@height.setter
def height(self): ...
1 | 如果非得基于 @property 来实现复用,我也可以继续用类装饰器(class decorator)或元类(metaclass)在创建类时介入处理,把普通属性自动替换为 property 对象来达到复用目的。但是,这种方案不但实现起来复杂,使用起来也不方便。 |
class Person:
def init(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
1 | 我们可以通过调用实例属性的方式来访问: |
class MappingMixin:
def getitem(self, key):
return self.dict.get(key)
def __setitem__(self, key, value):
return self.__dict__.set(key, value)
这个类可以让子类拥有像 dict 一样调用属性的功能
我们将这个 Mixin 加入到 Person 类中:
class Person(MappingMixin):
def init(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
现在 Person 拥有另一种调用属性方式了:
p = Person(“小陈”, “男”, 18)
print(p[‘name’]) # “小陈”
print(p[‘age’]) # 18
再定义一个 Mixin 类,这个类实现了 __repr__ 方法,能自动将属性与值拼接成字符串:
class ReprMixin:
def repr(self):
s = self.class.name + ‘(’
for k, v in self.dict.items():
if not k.startswith(‘_’):
s += '{}={}, ‘.format(k, v)
s = s.rstrip(’, ') + ‘)’ # 将最后一个逗号和空格换成括号
return s
利用 Python 的特性,一个类可以继承多个父类:
class Person(MappingMixin, ReprMixin):
def init(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
这样这个子类混入了两种功能:
p = Person(“小陈”, “男”, 18)
print(p[‘name’]) # “小陈”
print(p) # Person(name=小陈, gender=男, age=18)
- 总结
Mixin 实质上是利用语言特性,可以把它看作一种特殊的多重继承,所以它并不是 Python 独享,只要支持多重继承或者类似特性的都可以使用,比如 Ruby 中 include 语法,Vue 等前端领域也有 Mixin 的概念。
但 Mixin 终归不属于语言的语法,为了代码的可读性和可维护性,定义和使用 Mixin 类应该遵循几个原则:
Mixin 实现的功能需要是通用的,并且是单一的,比如上例中两个 Mixin 类都适用于大部分子类,每个 Mixin 只实现一种功能,可按需继承。
Mixin 只用于拓展子类的功能,不能影响子类的主要功能,子类也不能依赖 Mixin。比如上例中 Person 继承不同的 Mixin 只是增加了一些功能,并不影响自身的主要功能。如果是依赖关系,则是真正的基类,不应该用 Mixin 命名。
Mixin 类自身不能进行实例化,仅用于被子类继承。
## 元类
### __init_subclass__