1、描述器
实现了_set_、_get_和_del_方法的类成为描述器
具体描述器解析,可以查看:描述器解析
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 #这里是调用了上面的MyProperty描述器
def score(self):
return self._score
@score.setter
def set_score(self, value):
self._score = value
s = Student()
s.score = 95 #每当对象有变量赋值的时候,就会调用set和get方法
print(s.score)
结果输出:
注意:_XXX_这种名称的变量或者方法,都是系统默认的(称为魔术变量),没事不要修改,改了以后可能会修改系统的行为
2、特殊方法与特殊类(这些都是系统自带的,只要自己进行实现,就会更改类的行为)
(1)_str_函数
实现以后,会更改类的输出
class MyClass:
def __init__(self, name): #构造函数
self.name = name
print(MyClass('Tom'))
结果输出:
加了_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'))
输出结果:
(2)_iter_方法(还有加入_next_方法才可以)
实现将类对象变成迭代器
class Fib100:
def __init__(self): #构造方法
self._1, self._2 = 0, 1
def __iter__(self): #基本没有什么变化
return self
def __next__(self): #主要变化在这里,第一句话是主要变化
self._1, self._2 = self._2, self._1 + self._2
if self._1 > 100: #这里用来进行限制
raise StopIteration() #当大于100的时候,扔出一个异常
return self._1
for i in Fib100():
print(i)
输出结果:
1 1 2 3 5 8 13 21 34 55 89
(3)_getitem_方法
如果你创建一个类,那么你想通过下标来访问这个类,就要实现_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]) #显示1
print(f[5]) #显示8
print(f[10]) #显示89
# print(f[1:2:3])
(4)_call_方法(是否是可调用的)
class MyClass:
pass
cls = MyClass()
cls()
结果输出:
这样会报错,显示这个类的对象是不可以调用的,想要可调用,需要实现_call_方法:
class MyClass:
def __call__(self): #实现这个方法
print('You can call cls() directly.')
cls = MyClass()
cls()
print(callable(cls)) #判断这个对象是否是callable(可调用的)
print(callable(max)) #判断max是否是调用的
print(callable([1, 2, 3])) #判断一个列表是不是callable的
print(callable(None)) #判断None是不是一个callable的
print(callable('str')) #判断一个字符串是不是callable的
输出结果:
划线部分是调用以后输出的
3、枚举
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr'))
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value) #这里要说一下,print输出是用逗号隔开的
jan = Month.Jan
print(jan)
上面那个print输出,是要用逗号隔开的,之前我总是用+连接,结果总是报错,现在明白了,我去,总是按照java的思路来,也是晕了
4、元类
(1)用type创建一个类
def init(self, name): #定义一个函数
self.name = name
def say_hello(self): #又定义一个函数,下面要用到
print('Hello, %s!' % self.name)
# Hello = type('Hello', (object, ), dict(__init__ = init, hello = say_hello)) #这里和下面是等价的
Hello = type('Hello', (object, ), {'__init__':init, 'hello':say_hello}) #这是用type创建一个类
''' #这个类和上面用type创建的类是等价的
class Hello:
def __init__(...)
def hello(...)
'''
h = Hello('Tom')
h.hello() #调用hello方法,实质上是调用say_hello方法
上面介绍了一种另类的创建类方法,通过type来创建一个类,下面介绍一下参数的设置:
- 第一个参数“Hello”是类名;
- 第二个参数是继承类,继承自object(注意后面的逗号);
- 第三个参数是将函数一一对应,让Hello类的_init_初始化方法对应一开始创建的init方法,第二个hello方法对应第二个创建的say_hello方法(也就是说,调用hello方法的时候,就是在调用say_hello方法)
输出结果:
(2)元类
def add(self, value):
self.append(value)
class ListMetaclass(type): #元类一定是从type继承下来的
def __new__(cls, name, bases, attrs): #是在构造函数之前执行的
# print(cls)
# print(name)
# print(bases)
# print(type(attrs))
# attrs['add'] = lambda self, value: self.append(value)
attrs['add'] = add #给这个类,增加一个属性方法add
attrs['name'] = 'Tom' #给这个类增加一个属性变量name,变量的值是“Tom”
return type.__new__(cls, name, bases, attrs)
class MyList(list, metaclass = ListMetaclass): # 额外增加add方法,实际等价于append。
pass
mli = MyList()
mli.add(1)
mli.add(2)
mli.add(3)
print(mli.name)
print(mli)
没有怎么看懂,上面创建了一个MyList类,继承了list类和一个元类ListMetaclass,其实就是增加了一个add方法,但是为什么不直接加入这个函数呢,还要用元类呢?
这里主要讲的是继承的元类,这个元类主要有一个_new_方法,通过这个方法,你可以控制类的属性,其中的参数:
- name与bases参数是不能动的
- 主要可以修改的参数就是attrs,这是用来增加类的属性的,这个属性可以是属性方法,也可以是属性变量上面添加的add就是属性方法,而name则是属性变量
(3)
要解决的问题:
class User(Model): #继承自Model类
id = IntegerField('id')
name = StringField('name')
# u = User(id = 100, name = 'Tom')
u = User()
u.id = 100
u.name = 'Tom'
u.save() #显然save方法是来自Model类的,但是上面的id和name属性却是User独有的,不是其父类Model的
解决的问题很简单,一个类User继承了父类Model的方法save,但是它又新创建了两个变量,如果他想要在方法save中保存这两个变量,就必须要重写这个方法,那么有没有一个方法,可以让我们不用重新写这个方法呢,那就是要用元类了,具体实现如下:
#用来操作数据库的时候
class Field:
def __init__(self, name, col_type):
self.name = name #列名
self.col_type = col_type #类型
class IntegerField(Field): #继承上面的Field父类
def __init__(self, name):
super(IntegerField, self).__init__(name, 'integer') #调用父类上面Field的构造函数
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, 'varchar(1024)') #super调用父类构造函数
class ModelMetaclass(type): #元类
def __new__(cls, name, bases, attrs):
if name == 'Model': #如果类名是Model的话,就什么也不做,直接返回就可以了
return type.__new__(cls, name, bases, attrs)
print('Model name: %s' % name)
mappings = {} #定义一个字典
for k, v in attrs.items():
if isinstance(v, Field): #判断v是否是Field类型
print('Field name: %s' % k)
mappings[k] = v #是该类型,就加入字典中
for k in mappings.keys():
attrs.pop(k)
attrs['__mappings__'] = mappings #保留所有列信息
attrs['__table__'] = name #保留表名,也可以不是_table_,换成其他名字也可以
return type.__new__(cls, name, bases, attrs) #通过type创建一个新类,这个类中有上面_mappings_和_table_两个属性
class Model(dict, metaclass = ModelMetaclass):
def __init__(self, **kvs):
super(Model, self).__init__(**kvs)
def __getattr__(self, key):
try:
return self[key]
except KeyError: #如果key找不到的话
raise AttributeError("'Model' object has no attribute '%s'." % key)
def __setattr__(self, key, value):
print('__setattr__')
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:', sql)
print('args:', args)
class User(Model):
id = IntegerField('id') #有哪些列
name = StringField('name') #有哪些名字
# u = User(id = 100, name = 'Tom')
u = User()
u.id = 100
u.name = 'Tom'
u.save()
5、错误与异常处理
import traceback
try:
# r = 10 / 0
r = 10 / 1
except ZeroDivisionError as e:
print(e) #输出错误信息
r = 1
else:
print('没有异常')
finally:
print('不管有没有异常都执行')
print(r)
6、单元测试
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
参考自:Python入门基础——高级面向对象(视频教程)