一 . 使用@property
(1) Python内置的@porperty 装饰器就是负责把一个方法变成属性调用。
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 bettween 0~100!")
self._score=value
注:以上代码通过@property把一个getter方法变成属性,同时针对这个getter方法,@property会衍生出一个@score.setter的装饰器,用于把一个setter方法变成属性
s=Student()
s.score=60
s.score
同样,我们可以通过这种方式,对一个类创建只读属性
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self,value):
self._birth=value
@property
def age(self):
return 2019-self._birth
>>> s=Student()
>>> s.birth=1999
>>> s.birth
1999
>>> s.age
20
小结:
@property
def width(self):
return self._width
@width.setter
def width(self,value):
self._width=value
@property
def height(self):
return self._height
@height.setter
def height(self,value):
self._height=value
@property
def resolution(self):
return self._width*self._height
>>> s=Student()
>>> s.height=100
>>> s.height
100
>>> s.width=50
>>> s.width
50
>>> s.resolution
5000
小结:其实就是讲解如何通过@property快速对一个类的属性进行get和set方法的设置。
二,多重继承
(1) 通过继承,子类可以扩展父类的功能
>>> class Animal(object):
pass
>>> class Mammal(Animal):
pass
>>> class Bird(Animal):
pass
>>> class Dog(Mammal):
pass
>>> class Bat(Mammal):
pass
>>> class Parrot(Bird):
pass
>>> class Ostrich(Bird):
pass
>>> class Runnable(object):
def run(self):
print("Running...")
>>> class Flyable(object):
def fly(self):
print("Flying...")
>>> class Dog(Mammal,Runnable):
pass
>>> class Bat(Mammal,Flyable):
pass
(2) Mixln
1)在设计类的继承关系时,通常,主线都是单一继承下来
2) 通过多重继承可以实现增加额外的功能,这种继承称之为Mixin。
MixLn的目的就是给一个类增加多个功能,这样,在设计类的时候,优先考虑通过多重继承来着组合多个Mixln的功能,而不是
设计多层次的复杂的继承关系。
这样一来,我们不需要复杂而庞杂的继承连,只要选择组合不同的类的功能,就可以快速构造出所需的子类。
三,定制类
(1) Python的class中还有很多这样有特殊用途的函数,可以帮助我们定制类。
>>> print(Student('Michael'))
<__main__.Student object at 0x02CB37D0>
>>> class Student(object):
def __init__(self,name):
self.name=name
def __str__(self):
return 'Student object(name:%s)'%self.name
通过对Python对每个类的特殊函数根据需要进行定制化处理。
__str__ : 返回用户看到的字符串
__repr__:返回程序开发者看到的字符串。注:为调试服务
(2) 如果一个类想被用于for...in循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值
直到遇到StopIteration错误时退出循环。
__iter__:
>>> class Fib(object):
def __init__(self):
self.a,self.b=0,1
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
>>> for n in Fib():
print(n)
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
(3) Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,因为无法通过下标取出元素
要表现的像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
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101
>>>
但list有个神奇的切分方法:
>>> list(range(100))[5:10]
[5, 6, 7, 8, 9]
>>>
对于Fib却报错,原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice:
>>> class Fib(object):
def __getitem__(self,n):
if isinstance(n,int):
a,b=1,1
for x in range(n):
a,b=b,a+b
return a
if isinstance(n,slice):
start=n.start
stop=n.stop
if start is None:
start=0
a,b=1,1
L=[]
for x in range(stop):
if x>=start:
L.append(a)
a,b=b,a+b
return L
>>> f=Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
>>>
然后还要对step参数作处理,也没有对负数作处理。所以,要正确实现一个
__getitem__()还是有很多工作要做的。
(4) __getattr__
1)正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错
2)Python提供一个机制应对这种报错:
定制一个__getattr__()方法,动态返回一个属性:
>>> class Student(object):
def __init__(self):
self.name='Michael'
>>> s=Student()
>>> print(s.name)
Michael
>>> print(s.score)
Traceback (most recent call last):
File "<pyshell#384>", line 1, in <module>
print(s.score)
AttributeError: 'Student' object has no attribute 'score'
>>> 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
>>>
当调用不存在的属性是,Python解释器会试图调用__getattr__(self,'score')来尝试获得属性。
实例1:
>>> class Student(object):
def __getattr__(self,attr):
if attr=='age':
return lambda:25
此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。
要让class只响应特定的几个属性,我们按照约定,抛出AttributeError的错误:
25
>>> class Student(object):
def __getattr(self,attr):
if attr=='age':
return lambda:25
raise AttributeError('\'Student\'object has no attribute\'%s\''%attr)
>>>
(5) __call__
任何类,只需要定义一个__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()
My name is Michael
>>>
__call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样。所以你完全可以把对象看出函数,
把函数看成对象。
我们判断一个变量是对象还是函数是通过能否被调用,能被调用的对象就是一个Callable对象。
>>> callable(max)
True
>>> callable([1,2,3])
False
>>> callable(None)
False
>>> callable('str')
False
通过callable()函数,可以判断一个对象是否是"可调用'对象
三,使用枚举类
建立一个枚举类型的对象
>>> from enum import Enum
>>> Month=Enum('Month',('Jan','Feb','Mar','Apr','May','Jun','Aug','Sep','Oct','Nov','Dec'))
遍历一个枚举对象
>>> for name,member in Month.__members__.items():
print(name,'=>',member,',',member.value)
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Aug => Month.Aug , 7
Sep => Month.Sep , 8
Oct => Month.Oct , 9
Nov => Month.Nov , 10
Dec => Month.Dec , 11
也可以自己定义一个枚举类:
>>> from enum import Enum,unique
>>> @unique
class Weekday(Enum):
Sun=0
Mon=1
Tue=2
Wed=3
Thu=4
Fri=5
Sat=6
@unique 装饰器可以帮助我们检查保证没有重复值
四,使用元类
(1)动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的
而是运行时动态创建的。
(2)type()函数可以查看一个类型或变量的类型
class Hello(object):
def hello(self,name='World'):
print("Hello,%s"%name)
Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello
Python 是在运行时动态创建class,而创建class的方法就是使用type()函数
所以type()函数可以返回一个对象的类型,又可以创建出新的类型。
>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
(3)要创建一个class对象,type()函数依次传入3个参数:
1)class的名称
2)继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法
3)class的方法名称与函数绑定,例如,把函数fn绑定到方法名hello上
通过type()
函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class。
(4)metaclass
metaclass 直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
而创建出类,必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的‘实例’
(2) metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。
正常情况下,你不会碰到需要使用metaclass的情况。
>>> class ListMetaclass(type):
def __new__(cls,name,bases,attrs):
attrs['add']=lambda self,value:self.append(value)
return type.__new__(cls,name,bases,attrs)
>>> class MyList(list,metaclass=ListMetaclass):
pass
>P>>
当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,
要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,可以加上新的方法,
然后,返回修改后的定义。
__new__()方法接收到的参数依次是:
1. 当前准备创建的类的对象
2. 类的名字
3. 类继承的父类集合
4. 类的方法集合。
(3) 会遇到通过metaclass修改类定义的地方有:ORM
1)ORM 全称为“Object Relational Mapping”,即对象--关系映射
就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表。
好处在于不用直接操作SQL语句,写代码更简单。
2)要编写一个ORM框架,所有的类都只能动态定义。因为只有使用者才能根据表的结构定义出对应的类来。
有点小复杂,不是特别懂,以后慢慢掰扯吧。