您的位置:首页 > 编程语言 > Python开发

Python入门基础(9)——高级面向对象

2018-06-22 08:45 906 查看

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入门基础——高级面向对象(视频教程)


阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: