使用__slots_
限制实例可添加的属性, 但对当前类中的__slots__
对子类无作用:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'--报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'
#对子类不起作用
>>> class GraduateStudent(Student):
... pass
...
>>> g = GraduateStudent()
>>> g.score = 9999 #不报错
使用@property
@property
默认只赋予getter
属性, 如需setter
需要用到属性名.setter
:
@property
给一个Screen对象加上width
和height
属性,以及一个只读属性resolution
:
class Screen(object):
@property
def width(self):
return self._width
@property
def height(self):
return self._height
@property
def resolution(self):
return self._width*self._height
@height.setter
def height(self, x):
if isinstance(x, int):
self._height = x
else:
raise ValueError('check your value')
@width.setter
def width(self, x):
if isinstance(x, int):
self._width= x
else:
raise ValueError('check your value')
# 测试:
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
if s.resolution == 786432:
print('测试通过!')
else:
print('测试失败!')
多重继承
或者叫多继承,与java的多重继承区分。
实例方法的调用顺序按照拓扑序列的展开顺序。
类的继承关系通常主线都是单一继承下来的,例如,Ostrich
继承自Bird
。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich
除了继承自Bird
外,再同时继承Runnable
。这种设计通常称之为MixIn
。
定制类
__str__
通常__str__()
和__repr__()
代码都是一样的,所以,有个偷懒的写法:
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
__iter__
如果一个类想被用于for ... in
循环, 就必须实现一个__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 # 返回下一个值
#调用
>>> for n in Fib():
... print(n)
...
1
1
2
3
5
...
46368
75025
__getitem__
__getattr__
只有在没有找到属性的情况下,才调用__getattr__
,已有的属性,比如name
,不会在__getattr__
中查找。
__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.
__call__()
还可以定义参数;
可以把对象看成函数,把函数看成对象,因为两者之间本没有根本的区别;
callable()
函数,判断一个对象是否是“可调用”对象。
枚举类
from enum import Enum
:
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
把Student
的gender
属性改造为枚举类型,可以避免使用字符串:
# -*- coding: utf-8 -*-
from enum import Enum, unique
class Gender(Enum):
Male = 0
Female = 1
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = Gender(gender)
# 测试:
bart = Student('Bart', Gender.Male)
if bart.gender == Gender.Male:
print('测试通过!')
else:
print('测试失败!')
元类——暂时跳过
网站说以后用不到,以后在再学吧
错误调试与测试
错误处理
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
抛出错误
# err_raise.py
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
调试
assert
assert
的意思是,表达式n != 0
应该是True
,否则抛出AssertionError
logging
import logging
logging.basicConfig(level=logging.INFO) #指定记录的级别
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
#输出
$ python err.py
INFO:root:n = 0
Traceback (most recent call last):
File "err.py", line 8, in <module>
print(10 / n)
ZeroDivisionError: division by zero
允许你指定记录信息的级别,有debug
,info
,warning
,error
等几个级别,当我们指定level=INFO
时,logging.debug
不起作用; 指定level=WARNING
后,debug
和info
不起作用.
pdb
以参数-m pdb
启动后,pdb
定位到下一步要执行的代码-> s = '0'
。
输入命令l
: (Pdb) l
来查看代码;
n
: 可以单步执行代码;
输入命令 p
变量名来查看变量;
q
结束调试
$ python -m pdb err.py
> /Users/michael/Github/learn-python3/samples/debug/err.py(2)<module>()
-> s = '0'
pdb.set_trace()
执行到此行代码进入pdb
调试模式.
单元测试
读取实例属性: 实例名.属性
记住.
的作用是获取属性;
编写一个Dict类,这个类的行为和dict一致,但是可以通过属性来访问,用起来就像下面这样:
>>> d = Dict(a=1, b=2)
>>> d['a']
1
>>> d.a #通过属性访问
1
mydict.py
代码如下:
class Dict(dict):
def __init__(self, **kw):
super().__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
测试单元: unittest
模块,编写mydict_test.py
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
def test_init(self):
d = Dict(a=1, b='test')
self.assertEqual(d.a, 1)
self.assertEqual(d.b, 'test')
self.assertTrue(isinstance(d, dict))
def test_key(self):
d = Dict()
d['key'] = 'value'
self.assertEqual(d.key, 'value')
def test_attr(self):
d = Dict()
d.key = 'value'
self.assertTrue('key' in d) #注意
self.assertEqual(d['key'], 'value') #注意
def test_keyerror(self):
d = Dict()
with self.assertRaises(KeyError): #注意
value = d['empty']
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
运行单元测试:
#在mydict_test.py的最后加上两行代码:
if __name__ == '__main__':
unittest.main()
#然后当做正常的python脚本运行:
$ python mydict_test.py
#推荐方法:
#通过参数-m unittest直接运行单元测试:
$ python -m unittest mydict_test
.....
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
文档测试
上节课的例子用文档测试:
# mydict2.py
class Dict(dict):
'''
Simple dict but also support access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
def __init__(self, **kw):
super(Dict, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
if __name__=='__main__': #确保只在测试环境运行文档测试, 在引用模块不运行.
import doctest
doctest.testmod()