Python面向对象 -- slots, @property、多重继承MixIn、定制类(str, iter, getitem, getattr, call, callable函数,可调用对象)、元类(type, metaclass)
面向对象设计中最基础的3个概念:数据封装、继承和多态
动态给class增加功能
正常情况下,当定义了一个class,然后创建了一个class的实例后,可以在程序运行的过程中给该实例绑定任何属性和方法,这就是动态语言的灵活性。
(1)给一个实例绑定的方法,对另一个实例是不起作用的。
(2)为了给所有实例都绑定方法,可以给class绑定方法。
Student.set_age=set_age
注:是set_age,而不是set_age( )
(3) 给class绑定方法后,所有实例均可调用。
#定义了Student类
class Student(object):
pass #给实例s添加name属性
s=Student()
s.name="Jane"
print("s.name:",s.name) #给实例绑定一个方法
def set_age(self,age):
self.age=age from types import MethodType s.set_age=MethodType(set_age,s) s.set_age(23)
print("s.age:",s.age) #print("注意使用的是set_age,而不是set_age()")
Student.set_age=set_age s2=Student()
s2.set_age(45)
print("s2.age:",s2.age)
运行结果:
小结
通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。
使用_ _slots_ _
(1)__slots__是一个特殊变量,而不是一种方法
(2)为了达到限制实例的属性这一目的,在定义class时,定义一个_ _slots_ _特殊变量,来限制该class实例添加的属性:
注意:1、_ _slots_ _仅对当前类实例起作用,对继承的子类是不起作用的
2、子类中的属性=其子类本身的属性∪继承的父类中的属性,取并集
3、使用_ _slots_ _添加属性时,一定要给属性加上引号,因为用tuple绑定的是属性名称
4、给__slots__添加属性时,使用的是元组tuple
class Student(object):
__slots__=('name','age') #用tuple定义允许绑定的属性名称 s=Student() #创建一个实例
s.name='Maria'
s.age=23
# s.score=99 由于在类Student中限制了属性,所以添加score就会报错
print("s.name:",s.name)
print("s.age:",s.age) print("给s实例添加score属性后,报错类型为:")
try:
s.score=99
except AttributeError as e:
print("AttributeError:",e) print(' ')
print("下面定义了子类:")
class Graduate(Student): #继承了Student类,那么也继承了Student类的_ _slots_ _
__slots__=('score') #这在子类中也定义了_ _slots_ _
#看出下面g.name和g.age都正常,说明子类继承了Student类的_ _slots_ _
g=Graduate()
g.score=99
g.name='shirley'
g.age=34
print("g.name,g.age:",g.name,g.age)
print("g.score:",g.score)
运行结果:
使用__slots__变量后,子类中属性:取并集
父类中使用了__slots__变量:
a. 子类中没有使用该变量,取并集之后,子类中的属性仍然为所有属性
b. 子类中使用了__slots__变量,取并集之后,子类中的属性为子类中的属性+父类中的属性
注:如果父类中的属性定义了私有变量,子类中使用这个属性时,要注意变形
这个结论可以从上面的代码运行查看结果
@property
装饰器(decorator):
(1)可以给函数动态加上功能
(2)对于类的方法,@property 装饰器负责把一个方法变成属性调用
在类的方法中,如何使用@property?
把一个getter方法变成属性,只需要加上@property就可以了,此时,@property 本身又创建了另一个装饰器 @score.setter,负责把一个setter方法变成属性赋值。
只读属性:只定义getter方法,不定义setter方法的属性
可读写属性:既定义getter方法,又定义setter方法的属性
class Student(object): @property
def score(self): #为什么要用score,因为score是属性名
return self._score @score.setter
def score(self,value): #为什么要用score,因为score是属性名
if not isinstance(value,int):
raise ValueError("score must be an integer!")
if value<0 or value>100:
raise ValueError("score must between 0 ~ 100!")
self._score=value s=Student()
s.score=78 #OK,实际转化为s.set_score(60)
print("s.score:",s.score) #OK,实际转化为s.get_score()
运行结果:
多重继承
通过多重继承,一个子类就可以同时获得多个父类的所有功能
参考:https://kevinguo.me/2018/01/19/python-topological-sorting/
多重:多个, 多重继承:即同时继承多个对象
MixIn
(1)MixIn的目的就是给一个类增加多个功能。 解决对同一对象用不同标准来分类的问题。
举例:从性别划分,你是男人;从国家划分,你是中国人;从职业划分,你是程序员; mixin可以轻松定义具备跟你一样特征的人:中国男程序员(天朝屌丝逗比程序员)
(2)定义
在设计类的继承关系时,通常,主线都是单一继承下来的,但是,如果需要“混入”额外的功能,通过多重继承就 可以实现,这种设计通常称之为MixIn.
(3)小结
python允许使用多重继承,因此,MixIn就是一种常见的设计
只允许单一继承的语言(如JAVA),不能使用MixIn设计
定制类
__str__
将__str__方法理解成,规范化输出格式,输出用户能看懂的格式,在Python编辑器中调用print语句返回结果时使用
__repr__:在Python编辑器中直接输入调用变量返回结果时使用
直接显示变量调用的不是__str__( ),而是__repr__( ),两者的区别:
__str__( )返回用户看到的字符串
__repr__( )返回程序开发者看到的字符串,__repr__( )是为调试服务的
通常情况下,__repr__( )和 __str__( )的代码相同,所以偷懒时可以写成这种形式:__repr__=__str__ ,类似赋值,把__str__ 里的代码赋给__repr__
class Student(object):
def __init__(self,name):
self.name=name s = Student("Michael")
print(s)
运行结果:
下面使用了__str__方法后:
class Student(object):
def __init__(self,name):
self.name=name
def __str__(self):
return 'Student Object (name :%s) ' % self.name #返回的内容,自己可以修改 s=Student("Michael")
print(s)
返回用户能够看懂的格式:
__str__方法 return 后面的语句可以自己修改
class Student(object):
def __init__(self,name):
self.name=name
def __str__(self):
return "Hello, My name is %s" % self.name s=Student("Michael")
print(s)
__iter__
如果一个类想被用于for ……in 循环,类似list、tuple那样,就必须使用一个__iter__( )方法, 该方法返回一个迭代对象
举例:以斐波那契数列为例,写一个Fib类,可以作用于for循环:
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>500:
raise StopIteration()
return self.a for n in fib():
print(n)
__getitem__
Fib实例虽然能作用于for 循环,看起来和list有点像,但是,把它当成list来使用还是不行。比如,根据下标取对应位置的元素,需要实现__getiem__( )方法
class fib(object):
def __init__(self):
self.a,self.b=0,1 def __iter__(self): #__iter__方法,返回一个迭代对象
return self def __next__(self):
self.a,self.b=self.b,self.a+self.b
if self.a>100:
raise StopIteration
return self.a def __getitem__(self,n): #__getitem__方法
a,b=1,1
for x in range(n):
a,b = b, a+b
return a f=fib()
print('f[0]:',f[0])
print("f[1]:",f[1])
print("f[2]:",f[2])
print("f[3]:",f[3])
print("f[13]:",f[13])
运行结果:
__getattr__
正常情况下,当调用的类的方法或属性不存在时,就会报错。要避免这个错误,Python有一个机制,就是写一个__getattr__( )方法,动态返回一个属性。
注意:只有在没有找到属性的情况下,才调用__getattr__,已有的属性,不会在__getattr__中查找。
class Student(object):
def __init__(self,name):
self.name=name # 注:如果有多个属性查找不存在时,写在一起,而不是再重新写一个__getattr__方法
def __getattr__(self, attr):
if attr=='score':
return 78
if attr=='age':
return lambda :23 #因为lambda是一个函数,所以调用是要加上括号
raise AttributeError('\'Student\' object has no attrbuite :%s'%attr) s=Student("kitty")
print(s.name)
print(s.score)
print(s.age())
print(s.birth)
运行结果:
__call__
调用实例方法,方法 一:使用“实例.方法( )”,”“instance.method( )”形式来调用,如下代码:
class Student(object): def get_score(self):
return self._score def set_score(self,value):
if not isinstance(value,int):
raise ValueError("score must be an integer!")
if value<0 or value>100:
raise ValueError("score must between 0 ~ 100!")
self._score=value s=Student()
s.set_score(78)
#s是实例,调用实例的方法,格式:实例.方法(),instance.method()
print("s.score:",s.get_score())
调用实例的方法,方法二: 直接在实例本身上调用
如何实现:任何类,只需要定义一个__call( )方法,就可以直接对实例进行调用
(我的理解好像只有调用实例的call方法时,才可以直接对实例进行调用,而调用其他的方法时,
仍需要采用 " 实例.方法( )"这种格式。另外,下面代码中定义了__call__ 方法,直接在实例本身上调用时,也没看出什么特别的)
class Student(object):
def __init__(self, name,score):
self.name = name
self.score=score def __call__(self):
print('My name is %s.' % self.name)
print('My score is %s.' % self.score)
return "" #如果不加这一句,那么返回的结果会有None s=Student('Michael',89)
print(s())
print(s.name)
判断一个对象是否能被调用,能被调用的对象就是一个Callable对象。callable( )函数
函数和带有__call__( )类的实例的实例都是Callable对象
callable()函数,可以判断一个对象是否是“可调用”对象
class Student(object):
pass s=Student()
print("callable(s):",callable(s))
print("callable(max):",callable(max))
print("callable([1,2,3]):",callable([1,2,3]))
print("callable('str'):",callable('str'))
print("callable(None):",callable(None))
枚举类
from enum import Enum
enum:是一个模块,即一个Python文件,
Enum:是一个类名 , Enum( )相当于创建一个实例
Value属性则是自动赋给成员的int常量,默认从1开始计数
小结:Enum可以把一组相关常量定义在一个class钟,且class不可变,而且成员可以直接比较。
from enum import Enum Month=Enum('hello',('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')) for name, member in Month.__members__.items():
print(name,'==>',member,'==>',member.value) print(Month.Jan)
#print(hello.Jan)#会报错
运行结果:
实例:
#从Enum派生出自定义类
from enum import Enum,unique @unique #@unique装饰器可以检查保证没有重复值
class weekday(Enum):
Sun=0
Mon=1
Tue=2
Wed=3
Thu=4
Fri=5
Sat=6 print('weekday.Mon:',weekday.Mon)
print("weekday['Mon']:",weekday['Mon'])
#注意:因为中括号[]里有了单引号,所以外面要使用双引号括起来
print("weekday(1):",weekday(1))
print("weekday.Mon.value:",weekday.Mon.value)
运行结果:
实战:把Student类中的gender属性改成枚举类型,可以避免使用字符串:
from enum import Enum, unique
@unique
class Gender(Enum):
Male = 0
Female = 1 class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender bart = Student('Bart', Gender.Male)#创建一个实例,性别采用Gender类中的格式
if bart.gender == Gender.Male:
print(bart.gender)
print('测试通过!')
else:
print('测试失败!')
运行结果:
元类
type( )
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
作用:(1)可以查看一个类型或变量的类型
(2)创建出新的类
创建class对象时,type( )函数传入的3个参数:
a. 类名
b. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,记住元组tuple的单元素写法,要加上逗号
c. 类的方法名和函数绑定
个人理解:即使我们使用 class Xxx……这种格式来定义类,Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,仍会使用type ( )函数来创建类class.
metaclass 元类
根据metaclass创建出类,即是:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
metaclass允许创建类或者修改类
Python面向对象 -- slots, @property、多重继承MixIn、定制类(str, iter, getitem, getattr, call, callable函数,可调用对象)、元类(type, metaclass)的更多相关文章
- 线程的函数中调用MFC对话框类的变量
线程的函数中调用MFC对话框类的变量多线程传输文件的对话框 现在想要在对话框上添加一个进度条 为进度条映射变量m_progress这就需要在传输一段文件后就更新m_progress的值使进度条前进 也 ...
- Python面向对象高级编程-__slots__、定制类,枚举
当在类体内定义好各种属性后,外部是可以随便添加属性的,Python中类如何限制实例的属性? Python自带了很多定制类,诸如__slots__,__str__ __slots__ __slots__ ...
- python基础语法20 面向对象5 exec内置函数的补充,元类,属性查找顺序
exec内置函数的补充 exec: 是一个python内置函数,可以将字符串的代码添加到名称空间中; - 全局名称空间 - 局部名称空间 exec(字符串形式的代码, 全局名称空间, 局部名称空间) ...
- Python学习 Day 9 property 多重继承 Mixin
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改: s = Student() s.score = 9999 为了限制score的范围,可以通过一 ...
- Python - 面向对象编程 - @property
前言 前面讲到实例属性的时候,我们可以通过 实例对象.实例属性 来访问对应的实例属性 但这种做法是不建议的,因为它破坏了类的封装原则 正常情况下,实例属性应该是隐藏的,只允许通过类提供的方法来间接实现 ...
- python 面向对象七 property() 函数和@property 装饰符
一.property引入 为了使对象的属性不暴露给调用者和进行属性值检查,设置了访问属性的接口函数,使用函数访问属性,并可以在函数内部检查属性. >>> class Student( ...
- java 面向对象(四十二):反射(六)反射应用三:调用运行时类的指定结构
调用指定的属性: @Test public void testField1() throws Exception { Class clazz = Person.class; //创建运行时类的对象 P ...
- C++11 function用法 可调用对象模板类
std::function<datatype()> ()内写参数类型 datatype 代表function的返回值 灵活的用法.. 代码如下 #include <stdio.h&g ...
- Python元类之由浅入深
前言 元类属于python面向对象编程的深层次的魔法,非常重要,它使我们可以更好的掌控类从创建到消亡的整个生命周期过程.很多框架的源码中都使用到了元类.例如 Django Framework 中的 ...
随机推荐
- 入门wpf—— 3、样式
转载于:https://www.cnblogs.com/huangxincheng/category/388852.html 这个楼主写的很详解,也比较基础,刚学wpf的朋友看看很有帮助. 说起样式, ...
- Ole操作帮助类
/// <summary> /// Ole操作类 /// </summary> public class OleDataBaseHandle { private static ...
- FORM表单 onclick()与onsubmit()
FORM表单中onclick().submit()与onsubmit()的问题 最近遇到一次处理form数据的过滤,采用了button的onclick事件来检查,发现return false后表单仍然 ...
- 拓展 - WebRTC 多视频网络拓扑之三种架构
众所周知,WebRTC非常适合点对点(即一对一)的音视频会话.然而,当我们的客户要求超越一对一,即一对多.多对一设置多对多的解决方案或者服务,那么问题就来了:“我们应该采用什么样的架构?” .简单的呢 ...
- 利用position absolute使div居中
外层DIV{position:realtive}内层DIV{positon:absolute;top:50%;left:50%;margin-top:-100px;margin-left:-150px ...
- Spring IOC 总结
IOC 简介 IOC是(Inversion of Control,控制反转)的简写.Spring提供IOC容器,将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的的过度程序耦合.它由DI( ...
- idea 启动项目报错,more than one fragment with the name [spring web] was found
这是由于idea导入项目的时候有多个模块,并且有多个web.xml导致的,先删除对应的模块,后启动即可. 另外也有可能是spring的jar冲突,把冲突的jar删除即可.
- UBOOT2016.05 看门狗
硬件平台 AM335X UBOOT 2016.05 在UBOOT中关看门狗,需要修改屏蔽这两处代码 init_sequence_r->board_init->hw_watchdog_in ...
- Python并发编程-GIL全局解释器锁
Python并发编程-GIL全局解释器锁 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.GIL全局解释器锁概述 CPython 在解释器进程级别有一把锁,叫做GIL,即全局解释 ...
- MySQL/MariaDB数据库的触发器
MySQL/MariaDB数据库的触发器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.触发器概述 1>.什么是触发器 触发器的执行不是由程序调用,也不是由手工启动,而是 ...