使用slots

  • 如果想要限制实例的属性,如只允许对Student实例添加nameage属性;Python允许在定义class的时候,定义一个特殊的__slots__变量来限制该class实例能添加的属性

    1. class Student(object):
    2. __slots__ = ('name', 'age') #用tuple定义允许绑定的属性名称
  • 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

  • 除非在子类中也定义__slots__,这样子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

    使用@property

  • Python内置的@property装饰器就是负责把一个方法变成属性调用的

    1. class Student(object):
    2. @property
    3. def score(self):
    4. return self._score
    5. @score.setter
    6. def score(self, value):
    7. if not isinstance(value, int):
    8. raise ValueError('score must be an integer!')
    9. if value < 0 or value > 100:
    10. raise ValueError('score must between 0 ~ 100!')
    11. self._score = value
  • 把一个getter方法变成属性,只需要加上@property就可以,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值

  • 属性的方法名不要和实例变量重名,否则会造成无限递归,最终导致栈溢出报错

    多重继承

  • Python允许使用多重继承,Mixln就是一种常见的设计

  • 定义出肉食动物CarnivorousMixIn和植事动物HerbivoresMixIn,让某个动物同时拥有好几个MixIn

    1. class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
    2. pass

    定制类

    str

  • 打印出来的实例能够容易看出内部重要的数据

    1. class Student(object):
    2. def __init__(self, name):
    3. self.name = name
    4. def __str__(self):
    5. return 'Student object (name: %s)' % self.name
    6. #__repr__()是为调试服务的,加上下面这句第8行就可以不写print也能看到打印内容
    7. __repr__ = __str__
    8. print(Student('Michael')) #Student object (name: Michael)

    iter

  • iter()方法返回一个迭代对象,for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环

    1. class Fib(object):
    2. def __init__(self):
    3. self.a, self.b = 0, 1 # 初始化两个计数器a,b
    4. def __iter__(self):
    5. return self # 实例本身就是迭代对象,故返回自己
    6. def __next__(self):
    7. self.a, self.b = self.b, self.a + self.b # 计算下一个值
    8. if self.a > 100000: # 退出循环的条件
    9. raise StopIteration()
    10. return self.a # 返回下一个值

    getitem

  • 上面的Fib实例虽然能作用于for循环,但不能把它当成list来使用;若需要能够按照下标取出元素,需要实现__getitem__()

    1. class Fib(object):
    2. def __getitem__(self, n):
    3. if isinstance(n, int): # n是索引
    4. a, b = 1, 1
    5. for x in range(n):
    6. a, b = b, a + b
    7. return a
    8. if isinstance(n, slice): # n是切片
    9. start = n.start
    10. stop = n.stop
    11. if start is None:
    12. start = 0
    13. a, b = 1, 1
    14. L = []
    15. for x in range(stop):
    16. if x >= start:
    17. L.append(a)
    18. a, b = b, a + b
    19. return L
  • 与之对应还有__setitem__(),把对象视作list或dict来对集合赋值。还有一个__delitem__()方法用于删除某个元素

    getattr

  • 只有在没有找到属性的情况下,才调用__getattr__,只有在没有找到属性的情况下才调用__getattr__,已有的属性不会在__getattr__中查找

  • 任意的调用如s.abc都会返回None,因为__getattr__默认返回就是None;要让class只相应特定的几个属性,那么就要抛出AttributeError

    call

  • 任何类都可以通过定义一个__call__()方法以对实例进行调用

  • 能够被调用的对象就是一个Callable对象,比如函数和带有__call__()的类实例 ```python class Chain(object):

    def init(self, path=’’):

    1. self._path = path

    def getattr(self, path):

    1. return Chain('%s/%s' % (self._path, path))

    def str(self):

    1. return self._path

    repr = str

Chain().status.user.timeline.list #’/status/user/timeline/list’

  1. <a name="QEnBI"></a>
  2. ### 使用枚举类
  3. - Python提供了`Enum`来为枚举类型定义一个class类型,每个常量都是class的一个唯一实例;`value`属性则是自动赋给成员的`int`常量,默认从`1`开始计数;如果需要更精确地控制枚举类型,可以从`Enum`派生出自定义类,`@unique`装饰器可以检查保证没有重复值
  4. ```python
  5. from enum import Enum
  6. Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
  7. from enum import Enum, unique
  8. @unique
  9. class Weekday(Enum):
  10. Sun = 0 # Sun的value被设定为0
  11. Mon = 1
  12. Tue = 2
  13. Wed = 3
  14. Thu = 4
  15. Fri = 5
  16. Sat = 6

使用元类

type()

  • type()函数允许动态创建出类来,动态语言本身支持运行期动态创建类;一个class的类型就是type,而一个实例的类型是class
  • type()函数既可以返回一个对象的类型,又可以创建出新的类型,譬如可以通过type()函数创建出Hello类,而无需通过class Hello(object)...定义

    1. def fn(self, name='world'): # 先定义函数
    2. print('Hello, %s.' % name)
    3. Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class

    metaclass

  • 比较难,暂时先不管