py面向对象高级编程(__slots__限制动态添加属性、@property装饰器:可读可写属性、多重继承:TypeMixIn混入类名、定制类:__xxx__魔法方法)、枚举类enum.Enum()、

"""
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()










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值