Python学习笔记(面向对象高级编程)

1、使用__slots__限制实例的属性
     正常情况下,创建了类的实例后,可以给实例绑定任何属性和方法。
     例子:
class Student(object):
    pass
>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> def set_age(self, age): # 定义一个函数作为实例方法
...     self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法,使用MethodType
>>> Student.set_score = set_score  # 给class绑定方法,可以函数直接赋值,所有实例都能调用
         __slot__是特殊的变量,可以限制该class实例能添加的属性。但是只对当前类起作用,对继承类不起作用。
        例子:
class Student(object):

    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

2、使用@property
     @property装饰器就是负责把一个方法变成属性调用的。@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值。如果不设置setter那么这个方法变成的属性就是个只读属性。
     例子:
class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')

        self._score = value
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
Traceback (most recent call last):
  ...
ValueError: score must between 0 ~ 100!

3、多重继承
     一个子类可以同时继承多个父类,同时获得多个父类的所有功能。当一个子类需要多继承别的类来获得额外的功能时,成为MixIn,通过把类名加上MixIn,可以很好的说明这个类就是为了增加功能用的,也坑更好的看出继承关系。
      例子:
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):

    pass
      RunnableMixIn和CarnivorousMixIn就是用来增加功能的类。

4、定制类
     许多特殊用途的函数,__XXX__。
     __str__()打印实例的方法。
     例子:
>>> class Student(object):
...     def __init__(self, name):
...         self.name = name
...     def __str__(self):
...         return 'Student object (name: %s)' % self.name
...
>>> print(Student('Michael'))

Student object (name: Michael)
        __repr__()直接调用变量的方法。
       例子:
class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__
>>> s = Student('Michael')
>>> s
Student object (name: Michael)
        __iter__()
       如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
        例子:
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()

        return self.a # 返回下一个值
        __getitem__()
        要表现得像list那样按照下标取出元素,需要实现__getitem__()方法。
        例子:
class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
>>> f = Fib()
>>> f[0]
1
         __getattr__()
         在调用类的方法和属性时,会使用到这个方法。
         例子:
class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':

            return 99
>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99
        __call__()
        使实例像函数一样调用时,用到的方法。
        例子:
class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):

        print('My name is %s.' % self.name)
>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.

5、枚举类
     枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、状态等。枚举类型不可实例化,不可更改
      定义枚举时,成员名不允许重复

class Color(Enum):
    red = 1
    green = 2
    red = 3    # TypeError: Attempted to reuse key: 'red'
     成员值允许相同,第二个成员的名称被视作第一个成员的别名
class Color(Enum):
    red   = 1
    green = 2
    blue  = 1
print(Color.red)              # Color.red
print(Color.blue)             # Color.red
print(Color.red is Color.blue)# True
print(Color(1))               # Color.red  在通过值获取枚举成员时,只能获取到第一个成员
若要不能定义相同的成员值,可以通过 unique 装饰
from enum import Enum, unique
@unique
class Color(Enum):
    red   = 1
    green = 2
    blue  = 1  # ValueError: duplicate values found in <enum 'Color'>: blue -> red
可以通过成员名来获取成员也可以通过成员值来获取成员:
print(Color['red'])  # Color.red  通过成员名来获取成员
print(Color(1))      # Color.red  通过成员值来获取成员
每个成员都有名称属性和值属性:
member = Color.red
print(member.name)   # red
print(member.value)  # 1
支持迭代的方式遍历成员,按定义的顺序,如果有值重复的成员,只获取重复的第一个成员:
class Color(Enum):
    red   = 1
    green = 2
    blue  = 1
for color in Color:
    print(color)
#Color.red
#Color.green
特殊属性 __members__ 是一个将名称映射到成员的有序字典,也可以通过它来完成遍历:

class Color(Enum):
    red   = 1
    green = 2
    blue  = 1
for color in Color.__members__.items():
    print(color)          
#('red', <Color.red: 1>)
#('green', <Color.green: 2>)
#('blue', <Color.red: 1>)
for a,b in Color.__members__.items():
    print(a,b,b.value)   
#red Color.red 1
#green Color.green 2
#blue Color.red 1   
6、元
     Python的一切都是对象,类也是对象,元类就是用来创建这些类(对象)的,元类就是类的类。
type(10)
#> int
type([1,2,3])
#> list
type({'a'1'b'2})
#> dict
class DoNothing:
    pass
x = DoNothing()
type(x)
#> __main__.DoNothing
type(DoNothing)
#> type
class是由type这个类产生的实例。 type就是Python在背后用来创建所有类的元类。
type(name, bases, dict):
name: 字符串类型,存放新类的名字
bases: 元组(tuple)类型,指定类的基类/父类
dict: 字典类型,存放该类的所有属性(attributes)和方法(method)
我们可以调用 type(...) 来动态创建类。
Base = type('Base', (), {'counter'10})
Derived = type('Derived', (Base,), dict(get_counter=lambda selfself.counter))

x = Derived()
x.get_counter()
#> 10

__metaclass__属性,你可以在写一个类的时候为其添加__metaclass__属性。
class Foo(object): 
    __metaclass__ = something… 
如果你这么做了,Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下class Foo(object),但是类对象Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。
      自定义元类
      元类的主要目的就是为了当创建类时能够自动地改变类。有好几种方法可以办到,但其中一种就是通过在模块级别设定__metaclass__。
class Meta(type):
    def __new__(clsnamebasesnamespace, **kwargs):
        if name != 'Base' and 'bar' not in namespace:
            raise TypeError('bad user class')
        return super().__new__(cls, name, bases, namespace, **kwargs)

class Base(objectmetaclass=Meta):
    def foo(self):
        return self.bar()
元类Meta要求名字不是Base的类必须包含bar方法,Base在定义的时候传入关键字metacalss,指示
使用元类Meta来定制类。
现在,我们尝试定义一个不包含 bar 方法的Base子类,在类的定义(或者说生成)阶段就会报错:
>>> class Derived(Base):
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 1in <module>
  File "<stdin>", line 4in __new__
TypeError: bad user class

评论