第七章-面向对象高级编程
2017-04-28 18:17
302 查看
1 使用__slots__
在没有做任何限制的时候, 可以通过 实例.属性名 = 属性值
2) 给某个实例绑定方法
需要使用types下的MethodType来给实例绑定方法
实例.绑定成的方法名 = MethonType(现有的函数, 实例名)
注意此绑定的方法运行的是现有的那个函数, 是无法调用私有变量的
但是由于给实例绑定的方法, 别的实例无法使用
3) 给类绑定方法
通过上面的方法绑定的方法只能该实例使用, 要使得所有实例都可以使用需要给 类 绑定方法
类名.方法名 = 现有的函数名
具体绑定方法如下:
用元组来包裹可以定义的属性
但是__slots__只是限定了当前类, 不会对子类产生影响
针对常用的getter和setter函数, 有
在使用的时候是:
然而更加希望设置和获取 直接能像绑定的时候的使用的话, 就需要@property了
其中函数的名字最好改成变量名, @property表示getter, @函数名.setter就表示对应的setter了, 且该setter的函数名也要保持一致
经过这样的处理之后
如现在有 哺乳动物类Mammal, 它有子类Dog类, 但是由于Dog还能跑, 所以可以定义Runnable类, 并放入Dog的定义中. Dog继承自多个类, 也就是多重继承
像Dog是一直从Animal和Mammal一直逐步继承下来的, 但是需要新增Runnable这些的新功能, 此时用多重继承来加入Runnable的方式叫做Minin("混入")
通常为了标识, 将Runable后面加上Mixln来体现和区分, 也就是RunnableMixin
这样的规范就可以更加清楚的分辨继承的关系
定义__str__来修改打印对象的内容
但是__str__修改的是print()对象的时候的显示, 直接打印对象的时候显示相关的是__repr__(这个在命令行上才能看到, 这种情况是用于调试)
由于__str__和__repr__的功能一致, 最终写法如下:
此处的__iter__()和__next__()就是相当于iter()和next()的功能
iter返回本身就行, 有__iter__()才能具备迭代器的功能
具体实现输出值的常在__next__()中定义
使用情况如下
可以使用__getitem__()方法来完成随机获取的功能
具体关于斐波那契数列的修改如下
同时还有__setitem__, __delitem__两个方法用于设置值和删除值
调用__getattr__()方法是在实例参数中没有找到的情况下
但是此时如果获取的不是score则会返回None, 这样不好, 因而需要在别的情况下抛出一个异常
getattr的作用扩展RESTAPI
这样就可以在不修改Chain的情况下根据输入来设定path
判断一个对象是否可以被调用可以使用 callable() 函数
通过定义一个class类型, 每个常量就是class的唯一实例
Enum()需要传入两个参数, 一个是枚举类型的名字, 另一个是一个元组, 其中的内容是枚举类型中的值, 最终返回的给一个变量
可以通过该变量.__members__来访问枚举类型的具体数据
该变量.__members__ 是一个可迭代类型, 类似于字典形式, key是元组中的结果, value是第一个参数.元组中的元素, value.value是索引号(默认是1开始)
由此可以通过 Month名字 来调用各个枚举类型的值
另一种更加精确的定义方式是定义一个类继承自Enum, 并且使用装饰器unique来去除掉重复的值
具体的内容是 枚举标识=值 这样的形式定义
引用的时候直接使用 类名.枚举标识
访问枚举类型的方法:
使用type()可以查看类型, 其中type()里面查询的是类, 则返回的是 class type
实际上, type()既可以返回一个对象的类型, 又可以创建出新的类型
创建一个class对象, 需要传入3个对象:
1) class的名称
2) 继承的父类集合, 是tuple类型的
3) class的方法与函数绑定
此时使用type()创建的类与class创建的类本质是一样的, class定义时, 仅仅是扫描了一下class定义的语法, 然后调用type()创建出class
具体流程是:
先定义metaclass, 再创建类, 最后创建实例
按照默认习惯, metaclass的类名总是以Metaclass结尾
元类是类的模板, 需要继承自type, 且命名的时候以Metaclass结尾
使用元类作为模板的类其实就是继承自该元类, 写法是在继承列表里加入默认参数: metaclass=元类名字
使用元类模板的类在创建对象的时候回先调用 元类中的__new__()方法
因而元类需要定义__new__()方法, __new__()方法接收到的参数依次是:
1) 准备创建的类的对象
2) 类的名字
3) 父类的集合(元组形式)
4) 方法的集合(字典形式)
该元类返回的值需要用 type.__new__()来返回, 具体形式和利用type()函数生成对象类似, 只是多了第一个参数cls用于传递准备创建的对象
完成对对list绑定一个add方法完成append()函数功能的代码如下
1.1 绑定属性和方法
1) 来给实例绑定属性在没有做任何限制的时候, 可以通过 实例.属性名 = 属性值
class Student(object): pass >>> s = Student() >>> s.name = 'Michael' >>> print(s.name) Michael
2) 给某个实例绑定方法
需要使用types下的MethodType来给实例绑定方法
实例.绑定成的方法名 = MethonType(现有的函数, 实例名)
注意此绑定的方法运行的是现有的那个函数, 是无法调用私有变量的
但是由于给实例绑定的方法, 别的实例无法使用
>>> def set_age(self, age): ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s) >>> s.set_age(25) >>> s.age 25
3) 给类绑定方法
通过上面的方法绑定的方法只能该实例使用, 要使得所有实例都可以使用需要给 类 绑定方法
类名.方法名 = 现有的函数名
具体绑定方法如下:
>>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = set_score
1.2 限定绑定属性
由于Python可以随意的增加属性, 因而为了针对这种情况, 可以在定义函数的时候定义__slots__来限制类里面只有哪一系属性, 这样绑定别的属性就会报错.用元组来包裹可以定义的属性
但是__slots__只是限定了当前类, 不会对子类产生影响
class Student(object): __slots__ = ('name', 'age')
2 使用@property
@property是一个装饰器, 是用来设置 对象属性的获取,设置方式针对常用的getter和setter函数, 有
class Student(object): def get_score(self): return self._score def set_score(self, value): self._score = value
在使用的时候是:
whc = Student() whc.set_score(100) print(whc.get_score())
然而更加希望设置和获取 直接能像绑定的时候的使用的话, 就需要@property了
其中函数的名字最好改成变量名, @property表示getter, @函数名.setter就表示对应的setter了, 且该setter的函数名也要保持一致
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): self._score = value
经过这样的处理之后
>>> whc = Student() >>> whc.score = 60 # 等同于whc.set_score(60) >>> whc.score #等同于whc.get_score() 60
3 多重继承
Python支持多重继承如现在有 哺乳动物类Mammal, 它有子类Dog类, 但是由于Dog还能跑, 所以可以定义Runnable类, 并放入Dog的定义中. Dog继承自多个类, 也就是多重继承
class Mammal(object): pass class Runnable(object): def run(self): print('Running...') class Dog(Mammal, Runnable): pass
像Dog是一直从Animal和Mammal一直逐步继承下来的, 但是需要新增Runnable这些的新功能, 此时用多重继承来加入Runnable的方式叫做Minin("混入")
通常为了标识, 将Runable后面加上Mixln来体现和区分, 也就是RunnableMixin
class Dog(Mammal, RunnableMixIn): pass
这样的规范就可以更加清楚的分辨继承的关系
4 定制类
4.1 __str__和__repr__
正常打印对象的显示>>> class Student(object): ... def __init__(self, name): ... self.name = name ... >>> print(Student('Michael')) <__main__.Student object at 0x109afb190>
定义__str__来修改打印对象的内容
>>> class Student(object): ... def __init__(self, name): ... self.name = name ... def __str__(self): ... return 'Student object (name: %s)' % self.name ... >>> print(Student('Michael')) Student object (name: Michael)
但是__str__修改的是print()对象的时候的显示, 直接打印对象的时候显示相关的是__repr__(这个在命令行上才能看到, 这种情况是用于调试)
由于__str__和__repr__的功能一致, 最终写法如下:
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__
4.2 __iter__和__next__
如果想要使类能够在for..in中循环, 形成类似迭代器的功能, 就需要实现__iter__定义返回一个迭代对象, 定义__next__方法来返回每次调用返回的值此处的__iter__()和__next__()就是相当于iter()和next()的功能
iter返回本身就行, 有__iter__()才能具备迭代器的功能
具体实现输出值的常在__next__()中定义
class Fib(object): def __init__(self): self.a, self.b = 0, 1 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
4.3 __getitem__
既然可以让类实现遍历, 自然需要把类实现成list的成员访问, 切片操作等.可以使用__getitem__()方法来完成随机获取的功能
具体关于斐波那契数列的修改如下
class Fib(object): def __getitem__(self, n): if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L
同时还有__setitem__, __delitem__两个方法用于设置值和删除值
4.4 __getattr__
使用__getattr__可以用来防止用户调用不存在的变量的时候返回错误调用__getattr__()方法是在实例参数中没有找到的情况下
class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99
但是此时如果获取的不是score则会返回None, 这样不好, 因而需要在别的情况下抛出一个异常
class Student(object): def __getattr__(self, attr): if attr=='age': return lambda: 25 raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
getattr的作用扩展RESTAPI
class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): return Chain('%s/%s' % (self._path, path)) def __str__(self): return self._path __repr__ = __str__
这样就可以在不修改Chain的情况下根据输入来设定path
>>> Chain().status.user.timeline.list '/status/user/timeline/list'
4.5 __call__
如果想要设定 实例() 直接能够调用一个方法的话, 可以定义__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.
判断一个对象是否可以被调用可以使用 callable() 函数
>>> callable(Student()) True >>> callable(max) True >>> callable([1, 2, 3]) False >>> callable(None) False >>> callable('str') False
5 定义枚举类
Python3中可以引入enum中Enum来定义枚举型通过定义一个class类型, 每个常量就是class的唯一实例
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) # 输出结果 Jan => Month.Jan , 1 Feb => Month.Feb , 2 Mar => Month.Mar , 3 Apr => Month.Apr , 4 May => Month.May , 5 Jun => Month.Jun , 6 Jul => Month.Jul , 7 Aug => Month.Aug , 8 Sep => Month.Sep , 9 Oct => Month.Oct , 10 Nov => Month.Nov , 11 Dec => Month.Dec , 12
Enum()需要传入两个参数, 一个是枚举类型的名字, 另一个是一个元组, 其中的内容是枚举类型中的值, 最终返回的给一个变量
可以通过该变量.__members__来访问枚举类型的具体数据
该变量.__members__ 是一个可迭代类型, 类似于字典形式, key是元组中的结果, value是第一个参数.元组中的元素, value.value是索引号(默认是1开始)
由此可以通过 Month名字 来调用各个枚举类型的值
另一种更加精确的定义方式是定义一个类继承自Enum, 并且使用装饰器unique来去除掉重复的值
具体的内容是 枚举标识=值 这样的形式定义
引用的时候直接使用 类名.枚举标识
from enum import Enum, unique @unique #可以帮组我们检查来保证没有重复值 class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6
访问枚举类型的方法:
>>> day1 = Weekday.Mon >>> print(day1) Weekday.Mon >>> print(Weekday.Tue) Weekday.Tue >>> print(Weekday['Tue']) Weekday.Tue >>> print(Weekday.Tue.value) 2 >>> print(day1 == Weekday.Mon) True >>> print(day1 == Weekday.Tue) False >>> print(Weekday(1)) Weekday.Mon >>> print(day1 == Weekday(1)) True >>> Weekday(7) Traceback (most recent call last): ... ValueError: 7 is not a valid Weekday >>> for name, member in Weekday.__members__.items(): ... print(name, '=>', member) ... Sun => Weekday.Sun Mon => Weekday.Mon Tue => Weekday.Tue Wed => Weekday.Wed Thu => Weekday.Thu Fri => Weekday.Fri Sat => Weekday.Sat
6 使用元类
6.1 type()
Python这样的动态语言, 函数和类的定义, 不是在编译的时候定义的, 而是运行时动态的创建的使用type()可以查看类型, 其中type()里面查询的是类, 则返回的是 class type
>>> from hello import Hello >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class 'hello.Hello'>
实际上, type()既可以返回一个对象的类型, 又可以创建出新的类型
>>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '__main__.Hello'>
创建一个class对象, 需要传入3个对象:
1) class的名称
2) 继承的父类集合, 是tuple类型的
3) class的方法与函数绑定
此时使用type()创建的类与class创建的类本质是一样的, class定义时, 仅仅是扫描了一下class定义的语法, 然后调用type()创建出class
6.2 metaclass
metaclass译作元类, 是可以创建类的类具体流程是:
先定义metaclass, 再创建类, 最后创建实例
按照默认习惯, metaclass的类名总是以Metaclass结尾
6.2.1 通过元类给list添加add功能
元类的使用方法是:元类是类的模板, 需要继承自type, 且命名的时候以Metaclass结尾
使用元类作为模板的类其实就是继承自该元类, 写法是在继承列表里加入默认参数: metaclass=元类名字
使用元类模板的类在创建对象的时候回先调用 元类中的__new__()方法
因而元类需要定义__new__()方法, __new__()方法接收到的参数依次是:
1) 准备创建的类的对象
2) 类的名字
3) 父类的集合(元组形式)
4) 方法的集合(字典形式)
该元类返回的值需要用 type.__new__()来返回, 具体形式和利用type()函数生成对象类似, 只是多了第一个参数cls用于传递准备创建的对象
完成对对list绑定一个add方法完成append()函数功能的代码如下
class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) class MyList(list, metaclass=ListMetaclass): pass L = MyList() print(L) L.add(1) print(L)
6.2.2 通过元类创建ORM框架
具体代码如下# Field负责保存数据库表的字段名和字段类型 class Field(object): def __init__(self, name, column_type): self.name = name self.column_type = column_type def __str__(self): return '<%s:%s>' % (self.__class__.__name__, self.name) # 定义str类型的Field class StringField(Field): def __init__(self, name): super(StringField, self).__init__(name, 'varchar(100)') # 定义int类型的Field class IntegerField(Field): def __init__(self, name): super(IntegerField, self).__init__(name, 'bigint') # 编写元类 class ModelMetaclass(type): def __new__(cls, name, bases, attrs): if name == 'Model': return type.__new__(cls, name, bases, attrs) print('Found model: %s' % name) mappings = dict() for k, v in attrs.items(): if isinstance(v, Field): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v for k in mappings.keys(): attrs.pop(k) attrs['__mappings__'] = mappings # 保存属性和列的映射关系 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs) # 编写基类Model class Model(dict, metaclass=ModelMetaclass): def __init__(self, **kw): super(Model, self).__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'Model' object has no attribute '%s'" % key) def __setattr__(self, key, value): 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: %s' % sql) print('ARGS: %s' % str(args)) # 定义User类使用上述 class User(Model): # 定义类的属性到列的映射: id = IntegerField('id') name = StringField('username') email = StringField('email') password = StringField('password') # 创建一个实例: u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') # 保存到数据库: u.save() # 结果如下 # Found model: User # Found mapping: email ==> <StringField:email> # Found mapping: password ==> <StringField:password> # Found mapping: id ==> <IntegerField:uid> # Found mapping: name ==> <StringField:username> # SQL: insert into User (password,email,username,id) values (?,?,?,?) # ARGS: ['my-pwd', 'test@orm.org', 'Michael', 12345]
相关文章推荐
- python 面向对象高级编程
- 【Python】[面向对象高级编程] 多成继承,定制类,使用枚举
- C++面向对象高级编程(下)第一周-Geekband
- Geekband C++面向对象高级编程(下) 第二周笔记 暗影行者
- C++面向对象高级编程笔记02--GeekBand
- Python3 面向对象 高级编程
- python学习笔记(七) - 面向对象高级编程
- C++面向对象高级编程(下)第二周-Geekband
- 面向对象高级编程
- Geekband C++面向对象高级编程(上) 第二周笔记 暗影行者
- python之面向对象高级编程
- C++面向对象高级编程笔记01--GeekBand
- Python学习----面向对象高级编程
- Python面向对象高级编程——学习笔记
- Javascript高级程序设计第二版第六章--面向对象程序设计(Object-Oriented Programming)简称OOP编程--笔记
- 【Python】[面向对象高级编程] 使用__slots__,使用@property
- Python 第五章 面向对象高级编程
- python 面向对象高级编程
- Geekband C++面向对象高级编程(下) 第一周笔记 暗影行者
- Javascript高级程序设计第二版第六章--面向对象程序设计(Object-Oriented Programming)简称OOP编程--笔记