python—面向对象高级编程
补齐python3内容.
资料来源:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
更新
1
2019.01.17 初始化
面向对象高级编程
使用__slots__
动态语言可以随时修改和添加类与对象的属性和方法.python自然也不例外.
过于自由的修改,对类和对象本身不利,python中使用
__slots__
来约束这种行为.1
2class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称限制类中只能再增加 name 和 age属性.仅对 Student 类生效,对子类无效.
使用@property
装饰器,不仅在函数中其作用,还可以在类中使用,限定属性的读写和范围.
类属性的读写,都是通过get/set方式进行,太过繁琐.Python内置的
@property
装饰器,将一个方法变为属性调用.示例
1
2
3
4
5
6
7
8
9
10
11
12
13class Student(object):
def score(self):
return self._score
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
最终的赋值的终点是 self._score
.操作时候直接访问 xxx.score
即可.
限定只读,只定义getter,不定义setter.
1
2
3
4
5
6
7
8
9
10
11
12
13class Student(object):
def birth(self):
return self._birth
def birth(self, value):
self._birth = value
def age(self):
return 2015 - self._birthbirth 可写,age 只读.
多重继承
没什么多说的.
1
2class Bat(Mammal, Flyable):
pass多重继承的设计,相对java的单继承,自由度更大,但能不能用好,取决于写代码的人.
定制类
- 类中形如
__xxx__
的变量或者函数名
str && repr
__str__
和__repr__
是类/对象的打印输出. str 是调用print 函数的输出, repr 是 直接输出,常用于调试.示例:
1
2
3
4
5
6class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
iter && next
用于 for 循环,
__iter__
用于返回一个可迭代对象(通常是自身). 同时 for 循环时,不断调用__next__
,获取下一个结果,直到遇到StopIteration
错误.示例:
1
2
3
4
5
6
7
8
9
10
11
12class 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
通过
__getitem__
可以将一个对象,包装成一个list dict tuple.示例: list (包含不完整切片)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Fib(object):
def __getitem__(self, n):
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
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对于切片的负数 和 [:5] 没有处理.
dict时,相对应的还有
__setitem__()
__delitem__()
.主要是python是非强类型的语言,不必非要继承并实现接口.
getattr
当试图调用不存在的属性和方法时,报错.此时需要找
__getattr__
.__getattr__
中直接返回属性和函数均可.当未匹配时返回 None.也可以自定义返回的错误类型.示例
1
2
3
4
5
6class Student(object):
def __getattr__(self, attr):
if attr=='age':
return lambda: 25
raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)可以把一个类的所有属性和方法调用全部动态化处理(理解暂时不太清除,仅记录)
现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:
http://api.server/user/friends
http://api.server/user/timeline/list如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。
利用完全动态的__getattr__,我们可以写出一个链式调用
1
2
3
4
5
6
7
8
9
10
11
12class 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__1
2list Chain().status.user.timeline.
'/status/user/timeline/list'无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变!
call
python中函数与对象的区别十分模糊,所谓函数实际上可以看作是实现了
__call__
的一类对象,反过来一个对象通过添加(动态/静态)__call__
也可以当作函数调用.判断一个对象是否可当作函数调用,使用
callable
函数判断,可调用,返回True.示例:
1
2
3
4
5
6class Student(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)1
2
3'Michael') s = Student(
# self参数不要传入 s()
My name is Michael.
枚举类
枚举用于限定变量的值域,防止错误的赋值导致的严重后果.Python提供了Enum类.
简单定义
1
2
3from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))枚举类定义
1
2
3
4
5
6
7
8
9
10
11from enum import Enum, unique
# 防止值重复
class Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
元类
python作为动态语言,类和函数不是在编译时定义,而是运行时动态创建,除了编写代码创建类/函数外,python亦可以通过 type/metaclass 动态创建类/函数.
实际上,即使在python 中直接定义的类/函数 也是在运行时通过
type()
载入的,因此也可以直接通过type()
直接创建.示例:
1
2
3
4
5
6
7
8
9
10
11def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
...
type('Hello', (object,), dict(hello=fn)) # 创建Hello class, 类名, 父类 ,方法 Hello =
h = Hello()
h.hello()
Hello, world.
print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>metaclass
元类 ,简单来说,对象来源于类,而python中的 类 可以来源于metaclass
元类. 通过metaclass
可以非常简单的实现 对类的生成和修改.示例:习惯上 metaclass 的类名总是以 Metaclass 结尾
1
2
3
4
5
6
7
8
9
10# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
# 创建类的对象,类的名称,父类 , 方法集合
def __new__(cls, name, bases, attrs):
#方法添加 add 方法
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMetaclass):
pass创建 MyList 的时候,调用了
ListMetaclass.__new__()
, 在创建时添加了 add 方法.示例2:ORM数据库(简)
一个ORM数据库,需要动态的根据字段,生成类,与java中不同,python 非常简单.Field 基类,保存数据库的字段名和字段类型
1
2
3
4
5
6
7
8class 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)各个类型的Field
1
2
3
4
5
6
7
8
9class 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')ModelMetaclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':#如果是 Model 基类,不修改属性等.
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name)
mappings = dict()
for k, v in attrs.items():#保存 子类所有属性到mappings
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)基类Model
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class 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))使用
1
2
3
4
5
6
7
8
9
10
11class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 创建一个实例:
u = User(id=12345, name='Michael', email='[email protected]', password='my-pwd')
# 保存到数据库:
u.save()解
- 创建 User 时,查找 metaclass 未定义,继续查找父类 Model,执行父类的 metaclass .(子类会隐性继承父类的 metaclass ).
- 进入
ListMetaclass.__new__()
遍历 User 的属性,找出Field 类型的,写入 mappings .删除这些属性(防止干扰).,将SQL的表名 User 写入table字段. - .save调用时,遍历 mappings ,组合table 成相应SQL语句.(这里仅print)