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 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 self: self.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__(cls, name, bases, namespace, **kwargs):
if name != 'Base' and 'bar' not in namespace:
raise TypeError('bad user class')
return super().__new__(cls, name, bases, namespace, **kwargs)
class Base(object, metaclass=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 1, in <module>
File "<stdin>", line 4, in __new__
TypeError: bad user class
评论
发表评论