面向对象class的魔术函数
slots防止用户意外添加一些不想要的属性,限制当前类能添加的属性,不在slots会报错
import traceback
from types import MethodType
class MyClass(object):
__slots__ = ['name', 'set_name'] # 限制能添加的属性,只能添加name set_name
def set_name(self, name):
self.name = name
cls = MyClass()
cls.name = 'Tom'
cls.set_name = MethodType(set_name, cls)
cls.set_name('Jerry')
print(cls.name)
try:
cls.age = 30 # 添加age属性非法不在slot里,报AttributeError
except AttributeError:
traceback.print_exc()
# 但slots的属性限制功能只对当前class起作用,对子类无效
class ExtMyClass(MyClass):
pass
ext_cls = ExtMyClass()
ext_cls.age = 30 # 子类不受父类的slots限制,不会报错
print(ext_cls.age)
改变默认class返回classObject地址的行为,使其可以像str一样直接打印出来,实现str方法
class MyClass:
def __init__(self, name):
self.name = name
def __str__(self):
print('print will call __str__ first.')
return 'Hello ' + self.name + '!'
print(MyClass('Tom'))
实现一个类,支持next()迭代(即是Iterator,则一定是iterable的可用for遍历),需要实现2个方法:iter, next
class Fib100:
def __init__(self):
self._1, self._2 = 0, 1
def __iter__(self): # 通常直接返回self
return self
def __next__(self): # 关键在于next方法,在其中做真正的迭代运算
self._1, self._2 = self._2, self._1 + self._2
if self._1 > 100:
raise StopIteration() # 达到迭代结束条件,抛出StopIteration,系统会自动识别该异常
return self._1
for i in Fib100():
print(i)
自己实现一个class支持下标访问,就要实现__getitem__方法
class Fib:
def __getitem__(self, n):
# print(type(n))
a, b = 1, 1
for i in range(n):
a, b = b, a + b
return a
f = Fib()
print(f[1])
print(f[5])
print(f[10])
# print(f[1:2:3])
自定义class支持callable行为,等同c++中functor函数对象,必须实现call方法
class MyClass:
def __call__(self):
print('You can call cls() directly.')
cls = MyClass()
cls()
print(callable(cls))
print(callable(max))
print(callable([1, 2, 3]))
print(callable(None))
print(callable('str'))
借助Enum为了提高可读性
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr'))
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
jan = Month.Jan
print(jan)
用type 利用已有现成的方法,动态构造一个class
# 假设已经有现成的 init, say_hello方法,来构造新的类,且避免重复代码
def init(self, name):
self.name = name
def say_hello(self):
print('Hello, %s!' % self.name)
# 注意这种方式会报错,Python语法限制。(object)使得解释器无法区分是tuple还是单个元素,会被解析为单个元素
Hello = type('Hello', (object), {'__init__':init, 'hello':say_hello})
# tuple要这样(object, )
Hello = type('Hello', (object, ), {'__init__':init, 'hello':say_hello})
# Hello = type('Hello', (object, ), dict(__init__ = init, hello = say_hello))
''' 上述 Hello 的定义等效于下面的写法
class Hello:
def __init__(...)
def hello(...)
'''
h = Hello('Tom')
h.hello()
Python这种动态添加函数,构造class过程,其实是维护了一个class属性信息的表,可以灵活动态增加(怎么感觉有点类似c++的vptr??)
metaclass元类
def add(self, value):
self.append(value)
# metaclass元类一定是从type继承的,可用于控制class的创建过程,动态增加class的attrs表
metaclass控制类的创建
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
print(cls)
print(name)
print(bases)
print(type(attrs))
# attrs添加可以是lambda函数也可以是普通函数
# attrs['add'] = lambda self, value: self.append(value)
attrs['add'] = add
attrs['name'] = 'Tom'
return type.__new__(cls, name, bases, attrs) #必须要return type.new
class MyList(list, metaclass = ListMetaclass): # 额外增加add方法,实际等价于append。
pass
mli = MyList()
mli.add(1)
mli.add(2)
mli.add(3)
print(mli.name)
print(mli)
metaclass用于控制其他class的创建,最常用法就是对attrs修改,继承一个类型,比如上例中list
try-except-else-finally异常捕获
import traceback
try:
# r = 10 / 0
r = 10 / 1
except ZeroDivisionError as e:
print(e)
r = 1
else:
print('没有异常')
finally:
print('不管有没有异常都执行')
print(r)
使用unittest进行单元测试用例case, 并不需要实例化测试类
import unittest
class MyDict(dict):
pass
class TestMyDict(unittest.TestCase):
def setUp(self):
print('测试前准备')
def tearDown(self):
print('测试后清理')
def test_init(self):
md = MyDict(one = 1, two = 2)
self.assertEqual(md['one'], 1)
self.assertEqual(md['two'], 2)
# self.assertEqual(md['two'], 3)
def test_nothing(self):
pass
if __name__ == '__main__':
unittest.main()
# python test_module.py
# python -m unittest test_module
# python -m unittest test_module.test_class
# python -m unittest test_module.test_class.test_method
property描述器、装饰器
import traceback
class Student:
@property
def score(self):
return self._score
@score.setter # 装饰器
def score(self, value):
if not isinstance(value, int):
raise ValueError('not int')
elif (value < 0) or (value > 100):
raise ValueError('not between 0 ~ 100')
self._score = value
@property
def double_score(self):
return self._score * 2
s = Student()
s.score = 75
print(s.score)
try:
s.score = 'abc'
except ValueError:
traceback.print_exc()
try:
s.score = 101
except:
traceback.print_exc()
print(s.double_score)
try:
s.double_score = 150
except AttributeError:
traceback.print_exc()
class MyProperty:
def __init__(self, fget = None, fset = None, fdel = None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, cls):
if self.fget:
print('__get__')
return self.fget(instance)
def __set__(self, instance, value):
if self.fset:
print('__set__')
return self.fset(instance, value)
def __delete__(self, instance):
if self.fdel:
return self.fdel(instance)
def getter(self, fn):
self.fget = fn
def setter(self, fn):
self.fset = fn
def deler(self, fn):
self.fdel = fn
class Student:
@MyProperty
def score(self):
return self._score
@score.setter
def set_score(self, value):
self._score = value
s = Student()
s.score = 95
print(s.score)