"""
py面向对象高级编程.py
(__slots__限制动态添加属性、@property装饰器:可读可写属性、多重继承:TypeMixIn混入类名、定制类:__xxx__魔法方法)、枚举类enum.Enum()、元类metaclass:type、metaclass)
使用:
一、__slots__(限制动态添加属性)
1、#使用:types.MethodType()方法给 实例对象 动态添加绑定方法。
#注意:types.MethodType(,,)参数1是要绑定的方法,参数2是要绑定的实例对象,参数3是类名(可省略)
#注意:给 实例对象 动态添加绑定一个方法,但是对另一个实例对象是不起作用。
2、#使用:__slots__特殊方法用来 限制实例对象的属性 动态绑定添加,即:只允许对实例对象添加指定的属性。
#注意:__slots__定义的属性 的 动态添加绑定 仅对当前类实例起作用,对继承的子类是不起作用,
#除非子类也定义了__slots__字样,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
二、@property(@property装饰器为可读属性 / @装饰函数.setter装饰器为可写属性)
1、#使用:@property装饰器 负责把一个方法变成属性来调用,即:@property装饰器相当于getter方法可读属性。
2、#注意:@property装饰器 本身又会创建另一个装饰器,即:使用:@装饰函数.setter装饰器 相当于 setter可写属性。
三、多重继承(TypeMixin混入类名格式)
#通过多重继承,一个子类就可以同事获得多个父类的所有功能。
#使用:TypeMixIn格式 来定义多重继承的类名 即:类名混入格式。
四、定制类(__xxx__格式的魔法方法)
#使用:特殊属性和特殊方法(__xxx__格式的属性方法)可以在类定义中进行定制类操作。
#定制类官方参考网址:https://docs.python.org/zh-cn/3/reference/datamodel.html#special-method-names
1、#使用:__str__()魔法方法来定义print输出
#使用:__repr__()魔法方法来定义调试输出,即非print输出
2、#使用:__iter__及__next__魔法方法来定义 对象的迭代循环。
3、#使用:__getitem__来定义迭代对象的指定索引,即列表的索引
#注意:__getitem__通过实例对象的输入参数类型是否slice,来实现切片操作。
4、#使用:__getattr__魔法方法 动态返回一个属性
#注意:__getattr__只有在没找到属性的情况下才调用,已有的属性不会在__getattr__中查找。
5、#使用:__call__魔法方法 来使 类的实例对象可以直接实例对象()调用。即:实例对象() 的格式进行输出
#对实例对象进行直接调用就好比对一个函数进行调用一样,可以定义参数等。
#注意:实例对象调用 和 函数调用的概念区别,实质一样。
#使用:callable()函数来判断一个对象是否是 可调用对象,即:是否能被调用()。
五、枚举类enum.Enum()
#Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。
#使用enum.Enum()来创建一个枚举类,即:每个常量都是类的一个唯一实例。
六、元类metaclass(type、metaclass)
type()函数 既可以查看对象的类型,也可以动态创建类。
metaclass元类控制类的创建行为,根据metaclass创建出类,即:先定义metaclass,就可以创建类,最后创建实例。
metaclass允许创建类或者修改类,也可以把类看成是metaclass创建出来的“实例”。
1、#使用:type()函数 既可以查看对象的类型,也可以动态创建类。
#使用type()创建一个class对象,type()函数依次传入3个参数:
#参数1:class的名称;
#参数2:继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的元素写法;
#参数3:class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
#通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
2、#使用:metaclass元类控制类的创建行为,根据metaclass创建出类,即:先定义metaclass,就可以创建类,最后创建实例。
#metaclass允许创建类或者修改类,也可以把类看成是metaclass创建出来的“实例”。
#注意:默认metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass
3、#使用:类定义中传入 metaclass元类关键字参数 来指定从 元类.__new__()方法 来定制类。
"""
# =============================================================================
# #面向对象高级编程
# #面向对象编程的三个最基础的特征是:数据封装、继承、多态。
# #面向对象编程还有很多高级特性、特殊属性方法等。如:多重继承、定制类、元类等概念。
# =============================================================================
#################### __slots__(限制动态添加属性)
#使用:types.MethodType()方法给 实例对象 动态添加绑定方法。
#注意:types.MethodType(,,)参数1是要绑定的方法,参数2是要绑定的实例对象,参数3是类名(可省略)
#注意:给 实例对象 动态添加绑定一个方法,但是对另一个实例对象是不起作用。
#使用:__slots__特殊方法用来 限制实例对象的属性 动态绑定添加,即:只允许对实例对象添加指定的属性。
#注意:__slots__定义的属性 的 动态添加绑定 仅对当前类实例起作用,对继承的子类是不起作用,
#除非子类也定义了__slots__字样,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
#动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现
#可以分别给类 和 实例对象 动态添加绑定 属性和方法。
class Student(object):
pass
#####
#给 实例对象 动态添加绑定属性、方法。(添加属性直接赋值,添加方法通过types.MethodType()方法)
s=Student()
s.name='xiaoxiao' #给实例添加绑定属性
s.name
#使用:types.MethodType()方法给 实例对象 动态添加绑定方法。
#注意:types.MethodType(,,)参数1是要绑定的方法,参数2是要绑定的实例对象,参数3是类名(可省略)
def set_age(self,age):
"""定义一个函数,用作给实例对象添加绑定的方法"""
self.age=age
from types import MethodType
help(MethodType) #注意:MethodType(,,)参数1是要绑定的方法,参数2是要绑定的实例对象,参数3是类名(可省略)
s.set_age=MethodType(set_age,s) #给实例添加绑定一个方法,但是对另一个实例对象是不起作用。
s.set_age(25)
s.age
#注意:给 实例对象 动态添加绑定一个方法,但是对另一个实例对象是不起作用。
s2=Student()
#s2.set_age(26)
#####
#给 类 动态添加绑定属性、方法。(直接给类赋值方法:方法里定义属性)
def set_score(self,score):
"""定义一个函数,用作给 类 添加绑定方法;该 方法 相当于 创建了一个 属性"""
self.score=score
Student.set_score=set_score #给 类 动态添加绑定方法
s3=Student()
s3.set_score(100)
s3.score
s2.set_score(103)
s2.score
s.set_score(101)
s.score
#####
#使用:__slots__特殊方法用来 限制实例对象的属性 动态绑定添加,即:只允许对实例对象添加指定的属性。
#注意:__slots__定义的属性 的 动态添加绑定 仅对当前类实例起作用,对继承的子类是不起作用,
#除非子类也定义了__slots__字样,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
class Student(object):
"""#使用:__slots__特殊方法用来限制实例对象的属性添加,即:只允许对实例对象添加指定的属性。"""
__slots__=('name','age')
s=Student()
s.name='xiaowang'
s.age='18'
s.scure=99 #添加其他属性则引发属性异常。
#################### @property(@property装饰器为可读属性 / @装饰函数.setter装饰器为可写属性)
#使用:@property装饰器 负责把一个方法变成属性来调用,即:@property装饰器相当于getter方法可读属性。
#注意:@property装饰器 本身又会创建另一个装饰器,即:使用:@装饰函数.setter装饰器 相当于 setter可写属性。
class Student(object):
"""#使用:@property装饰器 负责把一个方法变成属性来调用;
即:@property装饰器相当于getter方法可读属性。"""
@property
def score(self):
return self._score
"""#注意:@property装饰器 本身又会创建另一个装饰器;
即:使用:@装饰函数.setter装饰器 相当于 setter可写属性。"""
@score.setter
def score(self,value):
if not isinstance(value,int):
raise ValueError('成绩需要是个整数')
if value<0 or value>100:
raise ValueError('成绩需要时0~100')
self._score=value
s=Student()
s.score=100
s.score
#当代码有了@property装饰器以后,我们就知道属性很可能不是直接暴露的,而是通过getter方式读取 和 setter方法写入的。
#定义只读属性,即:只定义@property装饰器(getter方法),补丁已setter方法。
#定义可读可写属性,即:定义@property装饰器 及 @装饰函数.setter装饰器
#如:
class Student(object):
"""定义可读可写的birth属性"""
@property
def birth(self):
return self.__birth
@birth.setter
def birth(self,value):
self.__birth=value
"""定义只读属性(无@装饰函数.setter装饰器)"""
@property
def age(self):
return 2015-self.__birth
#################### 多重继承(TypeMixin混入类名格式)
#通过多重继承,一个子类就可以同事获得多个父类的所有功能。
#使用:TypeMixIn格式 来定义多重继承的类名 即:类名混入格式。
#################### 定制类
#使用:特殊属性和特殊方法(__xxx__格式的属性方法)可以在类定义中进行定制类操作。
#定制类官方参考网址:https://docs.python.org/zh-cn/3/reference/datamodel.html#special-method-names
#使用:__str__()魔法方法来定义print输出
#使用:__repr__()魔法方法来定义调试输出,即非print输出
#使用:__iter__及__next__魔法方法来定义 对象的迭代循环。
#使用:__getitem__来定义迭代对象的指定索引,即列表的索引
#注意:__getitem__通过实例对象的输入参数类型是否slice,来实现切片操作。
#使用:__getattr__魔法方法 动态返回一个属性
#注意:__getattr__只有在没找到属性的情况下才调用,已有的属性不会在__getattr__中查找。
#使用:__call__魔法方法 来使 类的实例对象可以直接实例对象()调用。即:实例对象() 的格式进行输出
#对实例对象进行直接调用就好比对一个函数进行调用一样,可以定义参数等。
#注意:实例对象调用 和 函数调用的概念区别,实质一样。
#使用:callable()函数来判断一个对象是否是 可调用对象,即:是否能被调用()。
##########
#__str__(定义print输出)、__repr__(定义调试输出)
#使用:__str__()魔法方法来定义print输出
#使用:__repr__()魔法方法来定义调试输出,即非print输出
class Student(object):
def __init__(self,name):
self.name=name
def __str__(self):
""" #使用:__str__()魔法方法来定义print输出"""
return 'print输出内容:\nStudent Object (name:{})'.format(self.name)
"""将输出设置一致"""
# __repr__=__str__
def __repr__(self):
"""#使用:__repr__()魔法方法来定义调试输出,即非print输出"""
return '调试输出内容: \nStudent Object (name:{})'.format(self.name)
print(Student('Michael'))
Student('Michael')
##########
#__iter__、__next__(定义循环可迭代对象)
#使用:__iter__及__next__魔法方法来定义 对象的迭代循环。
#定义斐波那契数列类
class Fib(object):
def __init__(self):
self.a, self.b=0,1
def __iter__(self):
"定义迭代返回值,如果返回None则表示不可迭代"
return self #实例本身就是迭代对象,所有返回自己本身
def __next__(self):
self.a, self.b=self.b, self.a+self.b
if self.a>1000:
raise StopIteration()
return self.a
Fib()
for f in Fib():
print(f)
##########
#__getitem__(定义迭代的指定索引)
#使用:__getitem__来定义迭代对象的指定索引,即列表的索引
#注意:__getitem__通过实例对象的输入参数类型是否slice,来实现切片操作。
#使用:__getitem__来定义迭代对象的指定索引,即列表的索引
class Fib(object):
def __getitem__(self,n):
a,b=1,1
for x in range(n):
a,b=b,a+b
return a
f=Fib()
f[5] #像list那样按照下标取出元素
#注意:__getitem__通过实例对象的输入参数类型是否slice,来实现切片操作。
#定义迭代对象的 切片操作 slice
class Fib(object):
def __getitem__(self,n):
"""如果输入参数属于 int整数类型,则输出该索引对应的值"""
if isinstance(n,int):
a,b=1,1
for x in range(n):
a,b=b,a+b
return a
"""通过对 实例对象的参数判断 来实现切片操作;
即:如果输入参数属于 slice,则定义并返回新的列表"""
if isinstance(n,slice):
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来形成返回结果用的L列表,即切片的结果。
a,b=b,a+b
return L
f=Fib()
f[1:6]
##########
#__getattr__
#使用:__getattr__魔法方法 动态返回一个属性
#注意:__getattr__只有在没找到属性的情况下才调用,已有的属性不会在__getattr__中查找。
class Student(object):
def __init__(self):
self.name='Michael'
"""
#使用:__getattr__魔法方法 动态返回一个属性;
#注意:__getattr__只有在没找到属性的情况下才调用,已有的属性不会在__getattr__中查找。
"""
def __getattr__(self,attr):
if attr == 'score':
return 99
s=Student()
s.name
s.score
class Student(object):
def __getattr__(self,attr):
if attr == 'age':
return lambda:25 #返回函数也是完全可以的。只是需要注意调用的时候加上括号()
return AttributeError('\'Stuende类对象没有属性{}'.format(attr))
s=Student()
s.age() #注意调用的时候加上括号,因为__getattr__里定义返回的是函数。
s.time
"""没有定义__call__魔法方法的类不能直接通过类实例对象名()如函数般来调用"""
#s().time
#通过重写类的__getattr__ 及 __str__方法实现链式调用Api输出。
class Chain(object):
def __init__(self,path=''):
self._path=path
def __getattr__(self,path):
return Chain('{}/{}'.format(self._path, path))
def __str__(self):
return self._path
__repr__=__str__
Chain().status
Chain().users.ker945.git
##########
#__call__:实例对象()调用
#使用:__call__魔法方法 来使 类的实例对象可以直接实例对象()调用。即:实例对象() 的格式进行输出
#对实例对象进行直接调用就好比对一个函数进行调用一样,可以定义参数等。
#注意:实例对象调用 和 函数调用的概念区别,实质一样。
#使用:callable()函数来判断一个对象是否是 可调用对象,即:是否能被调用()。
class Student(object):
def __init__(self,name):
self.name=name
def __call__(self,age=10):
print('My name is {},My age is {}'.format(self.name,age))
s=Student('Michael')
"""定义__call__魔法方法的类,可以直接通过类实例对象名()如函数般来调用,否则不能直接类实例对象名()调用"""
s(11)
"""如果__call__类对象如函数般调用,则就模糊了对象和函数的界限;则通过callable()函数来判断对象是否可调用"""
callable(Student('xiaowang'))
callable(max)
callable(1)
callable('str')
#################### 枚举类enum.Enum()
#Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。
#使用enum.Enum()来创建一个枚举类,即:每个常量都是类的一个唯一实例。
#使用enum.Enum()来创建一个枚举类,即:每个常量都是类的一个唯一实例。
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)
Month.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
day1=Weekday.Mon
print(day1) #注意枚举类Enum的__str__和__repr___特殊方法不一致。
day1
#可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。
print(Weekday.Mon)
print(Weekday['Mon'])
print(Weekday.Mon.value)
print(Weekday(1))
day1==Weekday.Mon
day1==Weekday(1)
day1==Weekday.Sat
#################### 元类metaclass(type、metaclass)
#type()函数 既可以查看对象的类型,也可以动态创建类。
#metaclass元类控制类的创建行为,根据metaclass创建出类,即:先定义metaclass,就可以创建类,最后创建实例。
#metaclass允许创建类或者修改类,也可以把类看成是metaclass创建出来的“实例”。
#####
#使用:type()函数 既可以查看对象的类型,也可以动态创建类。
#使用type()创建一个class对象,type()函数依次传入3个参数:
#参数1:class的名称;
#参数2:继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的元素写法;
#参数3:class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
#通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
def fn(self,name='world'):
print('Hello,{}'.format(name))
Hello=type('Hello',(object,),dict(hello=fn))
h=Hello()
h.hello()
#####
#使用:metaclass元类控制类的创建行为,根据metaclass创建出类,即:先定义metaclass,就可以创建类,最后创建实例。
#metaclass允许创建类或者修改类,也可以把类看成是metaclass创建出来的“实例”。
#注意:默认metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass
#注意:metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
"""定义一个类,用来作为后期创建类的元类。
定义__new__方法,来给接下来根据此元类定义的Mylist类增加一个 add功能
__new__()的attr参数4表示类的方法的集合"""
def __new__(cls, name, bases, attrs):
"""通过对__new__()的attr参数3的类的方法的集合进行添加函数,来实现增加功能 """
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs) #返回type()创建类的__new__方法。
#####
#使用:__new__魔法方法定义类创建过程。
#__new__()方法接收到的参数依次是:
#参数1:当前准备创建的类的对象;
#参数2:类的名字;
#参数3:类继承的父类集合;
#参数4:类的方法集合。
#####
#使用:类定义中传入 metaclass元类关键字参数 来指定从 元类.__new__()方法 来定制类。
#如下:当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建
class Mylist(list, metaclass= ListMetaclass):
pass
l=Mylist() #测试通过元类定义增加了add功能的Mylist类的实例功能
l.add(1)
l
#####
#示例 构建ORM框架
#通过metaclass元类修改类定义,构建ORM框架。
#ORM全称“Object Relational Mapping”,即对象-关系映射,
#就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。
#实现ORM框架:
#定义元类ModelMetaclass
#编写最复杂的元类ModelMetaclass,即元类定义:
class ModelMetaclass(type):
def __new__(cls,name,bases,attrs):
if name == 'Model':
return type.__new__(cls,name,bases,attrs)
print('Found model: {}'.format(name))
mappings=dict()
for k,v in attrs.items():
if isinstance(v,Field):
print('Found mapping:{} ==> {}'.format(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)
#根据元类来定义基类:
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 '{}'".format(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 {} ({}) values ({})' .format(self.__table__, ','.join(fields), ','.join(params))
print('SQL: {}'.format(sql))
print('ARGS: {}'.format(str(args)))
#使用:super函数 调用父类方法。
#使用:super(子类名,self).__init__() 来显示调用 父类初始化方法
#注意:Python3可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
#定义Field字段类,负责保存数据库表的字段名和字段类型
class Field(object):
def __init__(self,name,column_type):
self.name=name
self.column_type=column_type
def __str__(self):
return '<{}:{}>'.format(self.__class__.__name__, self.name)
#在Field类的基础上,进一步定义各种类型的Field,比如StringField,IntegerField等。
class StringField(Field):
def __init__(self,name):
super(StringField,self).__init__(name,'vARCHAR(100)')
class IntegetField(Field):
def __init__(self,name):
super(IntegetField,self).__init__(name,'bigint')
#编写调用接口。
#比如,使用者如果使用这个ORM框架,想定义一个User类来操作对应的数据库表User,如下代码:
class User(Model):
id=IntegetField('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()
py面向对象高级编程(__slots__限制动态添加属性、@property装饰器:可读可写属性、多重继承:TypeMixIn混入类名、定制类:__xxx__魔法方法)、枚举类enum.Enum()、
最新推荐文章于 2023-02-09 11:51:16 发布