9.面向对象高级编程
9.1使用__slots__
在动态语言中,实例可以轻松绑定一个属性,但直接绑定一个方法,对另一个实例是不起作用的
>>> def set_age(self
, age
):
... self
.age
= age
...
>>> from types
import MethodType
>>> s
.set_age
= MethodType
(set_age
, s
)
>>> s
.set_age
(25)
>>> s
.age
25
>> s2
= Student
()
>>> s2
.set_age
(25)
Traceback
(most recent call last
):
File
"<stdin>", line
1, in <module
>
AttributeError
: 'Student' object has no attribute
'set_age'
给class绑定方法
>>> def set_score(self
, score
):
... self
.score
= score
...
>>> Student
.set_score
= set_score
使用__slot__限制实例的属性
class Student(object):
__slots__
= ('name', 'age')
>>> s
= Student
()
>>> s
.name
= 'Michael'
>>> s
.age
= 25
>>> s
.score
= 99
Traceback
(most recent call last
):
File
"<stdin>", line
1, in <module
>
AttributeError
: 'Student' object has no attribute
'score'
注意:对继承的子类无效,除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__
9.2使用@property
限制属性范围的常规操作
class Student(object):
def get_score(self
):
return self
._score
def set_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
使用@property装饰器把一个方法变成属性调用(setter/getter)
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
可定义只读属性,只定义getter
class Student(object):
@
property
def birth(self
):
return self
._birth
@birth
.setter
def birth(self
, value
):
self
._birth
= value
@
property
def age(self
):
return 2015 - self
._birth
9.3多重继承
e.g.
┌───────────────┐
│ Animal │
└───────────────┘
│
┌────────────┴────────────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Mammal │ │ Bird │
└─────────────┘ └─────────────┘
│ │
┌─────┴──────┐ ┌─────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ MRun │ │ MFly │ │ BRun │ │ BFly │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Dog │ │ Bat │ │ Ostrich │ │ Parrot │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
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
MixIn正常继承关系都是单一继承下来,但如果需要“”混入“额外功能,就需要MixIn,类似于Java中的接口
class Dog(Mammal
, RunnableMixIn
, CarnivorousMixIn
):
pass
9.4定制类
__len__
def __len__(self
):
return 100
__str__
def __str__(self
):
return 'Student object (name: %s)' % self
.name
__repr__
= __str__
__iter__,用于for...in循环,返回一个Iterator迭代对象,Python就会不断调用对象的__next__获取下一个循环值,直到出翔StopIteration错误
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
__getitem__,用于像list一样可以取出元素
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
进一步匹配list的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
还有__setitem__()方法可以把对象当作list/dict来对集合赋值,__delitem__()方法,用于删除某个元素
__getattr__在没有对应属性/方法的情况下返回
class Student(object):
def __getattr__(self
, attr
):
if attr
=='score':
return 99
完全动态的__getattr__链式调用
class Chain(object):
def __init__(self
, path
=''):
self
._path
= path
def __getattr__(self
, path
):
return Chain
('%s/%s' % (self
._path
, path
))
def __str__(self
):
return self
._path
__repr__
= __str__
>>> Chain
().status
.user
.timeline
.list
'/status/user/timeline/list'
__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
.
9.5使用枚举类
使用Enum类来定义常量(每个常量都是class的一个唯一实例)
from enum
import Enum
Month
= Enum
('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:
for name
, member
in Month
.__members__
.items
():
print(name
, '=>', member
, ',', member
.value
)
>>>
Jan
=> Month
.Jan
, 1
Feb
=> Month
.Feb
, 2
Mar
=> Month
.Mar
, 3
...
Enum自定义派生类
from enum
import Enum
, unique
@unique
class Weekday(Enum
):
Sun
= 0
Mon
= 1
Tue
= 2
Wed
= 3
Thu
= 4
Fri
= 5
Sat
= 6
>>> day1
= Weekday
.Mon
>>> print(day1
)
Weekday
.Mon
>>> print(Weekday
.Tue
)
Weekday
.Tue
>>> print(Weekday
['Tue'])
Weekday
.Tue
>>> print(Weekday
.Tue
.value
)
2
>>> print(Weekday
(1))
Weekday
.Mon
9.6使用元类
9.6.1type()
type()函数可以用于动态创建类,无需class…来定义
>>> def fn(self
, name
='world'):
... print('Hello, %s.' % name
)
...
>>> Hello
= type('Hello', (object,), dict(hello
=fn
))
>>> h
= Hello
()
>>> h
.hello
()
Hello
, world
.
type(类名,继承的父类集合tuple,方法名称与函数绑定)
9.6.2metaclass
metaclass用于控制类的创建行为类→实例,metaclass→类→实例创建一个list
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
>>> L
= MyList
()
>>> L
.add
(1)
>> L
[1]
ORM框架应用(ORM:对象关系映射)
class Field(object):
def __init__(self
, name
, column_type
):
self
.name
= name
self
.column_type
= column_type
def __str__(self
):
return '<%s:%s>' % (self
.__class__
.__name__
, self
.name
)
class StringField(Field
):
def __init__(self
, name
):
super(StringField
, self
).__init__
(name
, 'varchar(100)')
class IntegerField(Field
):
def __init__(self
, name
):
super(IntegerField
, self
).__init__
(name
, 'bigint')
class ModelMetaclass(type):
def __new__(cls
, name
, bases
, attrs
):
if name
=='Model':
return type.__new__
(cls
, name
, bases
, attrs
)
print('Found model: %s' % name
)
mappings
= dict()
for k
, v
in attrs
.items
():
if isinstance(v
, Field
):
print('Found mapping: %s ==> %s' % (k
, v
))
mappings
[k
] = v
for k
in mappings
.keys
():
attrs
.pop
(k
)
attrs
['__mappings__'] = mappings
attrs
['__table__'] = name
return type.__new__
(cls
, name
, bases
, attrs
)
class Model(dict, metaclass
=ModelMetaclass
):
def __init__(self
, **kw
):
super(Model
, self
).__init__
(**kw
)
def __getattr__(self
, key
):
try:
return self
[key
]
except KeyError
:
raise AttributeError
(r
"'Model' object has no attribute '%s'" % key
)
def __setattr__(self
, key
, value
):
self
[key
] = value
def save(self
):
fields
= []
params
= []
args
= []
for k
, v
in self
.__mappings__
.items
():
fields
.append
(v
.name
)
params
.append
('?')
args
.append
(getattr(self
, k
, None))
sql
= 'insert into %s (%s) values (%s)' % (self
.__table__
, ','.join
(fields
), ','.join
(params
))
print('SQL: %s' % sql
)
print('ARGS: %s' % str(args
))
class User(Model
):
id = IntegerField
('id')
name
= StringField
('username')
email
= StringField
('email')
password
= StringField
('password')
u
= User
(id=12345, name
='Michael', email
='test@orm.org', password
='my-pwd')
u
.save
()
参考教程
廖雪峰老师的Python3教程
(转载整理自网络,如有侵权,联系本人删除,仅供技术总结使用)