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入门基础——高级面向对象(视频教程)
相关文章推荐
- python基础---面向对象高级
- Python3 基础:面向对象高级编程(上)
- Python3 基础:面向对象高级编程(中)
- 【七月Python入门】 第四课面向对象基础
- python入门6面向对象高级编程
- Python基础之六面向对象高级编程
- 《每日一课》Python入门之面向对象高级编程
- Python基础7:面向对象2
- Python面向对象基础知识
- python 面向对象高级编程
- python3 从基础入门到高级进阶练习题之----基础篇001
- 【Python】学习笔记——-8.1、面向对象高级编程:1.使用__slots__
- python第六天学习记录——面向对象基础
- python——面向对象基础
- Python 面向对象高级编程――使用枚举和元类
- python 2.7 : 面向对象高级编程
- python面向对象基础
- C# 在线培训之零基础入门 06:面向对象入门
- 【廖雪峰Python习题集】面向对象高级编程
- PYTHON面向对象入门