zoukankan      html  css  js  c++  java
  • Python面向对象

    实例属性和类属性
    给实例绑定属性的方法是通过实例变量,或者通过self变量:
    class Student(object):
        def __init__(self, name):
            self.name = name
    
    s = Student('Bob')
    s.score = 90
    
    但是,如果Student类本身需要绑定一个属性呢?可以直接在class中定义属性,这种属性是类属性,归Student类所有:
    class Student(object):
        name = 'Student'
        
    当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。来测试一下:
    >>> class Student(object):
    ...     name = 'Student'
    ...
    >>> s = Student() # 创建实例s
    >>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
    Student
    >>> print(Student.name) # 打印类的name属性
    Student
    >>> s.name = 'Michael' # 给实例绑定name属性
    >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
    Michael
    >>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
    Student
    >>> del s.name # 如果删除实例的name属性
    >>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
    Student 
    
    
    访问限制
    在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。
    但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性:
    
    >>> bart = Student('Bart Simpson', 59)
    >>> bart.score
    59
    >>> bart.score = 99
    >>> bart.score
    99
    
    如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,
    在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,
    所以,我们把Student类改一改:
    class Student(object):
        def __init__(self, name, score):
            self.__name = name
            self.__score = score
    
        def print_score(self):
            print('%s: %s' % (self.__name, self.__score))
            
    改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name和实例变量.__score了>>> bart = Student('Bart Simpson', 59)
    >>> bart.__name
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute '__name'
    
    这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
    但是如果外部代码要获取name和score怎么办?可以给Student类增加get_name和get_score这样的方法:
    class Student(object):
        ...
    
        def get_name(self):
            return self.__name
    
        def get_score(self):
            return self.__score
            
    如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score方法:
    class Student(object):
        ...
        def set_score(self, score):
            self.__score = score
            
    你也许会问,原先那种直接通过bart.score = 99也可以修改啊,为什么要定义一个方法大费周折?
    因为在方法中,可以对参数做检查,避免传入无效的参数:
    class Student(object):
        ...
        def set_score(self, score):
            if 0 <= score <= 100:
                self.__score = score
            else:
                raise ValueError('bad score')
                
    需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,
    特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。
    有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,
    但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
    
    双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。
    不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,
    仍然可以通过_Student__name来访问__name变量:
    
    >>> bart._Student__name
    'Bart Simpson'
    
    但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。
    
    总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。
    
    最后注意下面的这种错误写法:
    >>> bart = Student('Bart Simpson', 59)
    >>> bart.get_name()
    'Bart Simpson'
    >>> bart.__name = 'New Name' # 设置__name变量!
    >>> bart.__name
    'New Name'
    
    表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!
    内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量。不信试试:
    
    >>> bart.get_name() # get_name()内部返回self.__name
    'Bart Simpson'
    
    
    
    实例方法、类方法、静态方法区别
    一、关于参数的区别
    实例方法:定义实例方法是最少有一个形参 ---> 实例对象,通常用 self
    类方法:定义类方法的时候最少有一个形参 ---> 类对象,通常用 cls
    静态方法:定义静态方法的时候可以不定义形参
    
    二、关于方法定义时候添加装饰器的区别
    实例方法:不需要添加装饰器
    类方法:需要添加装饰器 ----> @classmethod
    静态方法:需要添加装饰器 ---> @staticmethod
    
    三、调用:
    1.实例方法可以通过对象直接调用
    2.但是用类名调用的时候,需要创建一个对象,并且在传递参数的时候要将对象传递进去
    3.类方法可以通过类名直接调用,也可以通过对象来调用
    4.静态方法可以通过类名直接调用,也可以通过对象来调用
    
    四、补充
    1.静态方法不可以继承  
    2.类方法不能访问实例变量,只能访问类变量
    
    
    
    类的组合
    组合:组合指的是,在一个类中以另外一个类的对象(也就是实例)作为数据属性,称为类的组合
    也就是说:一个类的属性是另一个类的对象,就是组合
    
    圆环使用案例:
    from math import pi
    
    class Circle:
        '''
        定义了一个圆形类;
        提供计算面积(area)和周长(perimeter)的方法
        '''
        def __init__(self,radius):
            self.radius = radius
    
        def area(self):
             return pi * self.radius * self.radius
    
        def perimeter(self):
            return 2 * pi *self.radius
    
    
    circle =  Circle(10) #实例化一个圆
    area1 = circle.area() #计算圆面积
    per1 = circle.perimeter() #计算圆周长
    print(area1,per1) #打印圆面积和周长
    
    class Ring:
        '''
        定义了一个圆环类
        提供圆环的面积和周长的方法
        '''
        def __init__(self,radius_outside,radius_inside):
            self.outsid_circle = Circle(radius_outside)
            self.inside_circle = Circle(radius_inside)
    
        def area(self):
            return self.outsid_circle.area() - self.inside_circle.area()
    
        def perimeter(self):
            return  self.outsid_circle.perimeter() + self.inside_circle.perimeter()
    
    
    ring = Ring(10,5) #实例化一个环形
    print(ring.perimeter()) #计算环形的周长
    print(ring.area()) #计算环形的面积
    
    
    
    继承和多态
    在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),
    而被继承的class称为基类、父类或超类(Base class、Super class)。
    比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印:
    class Animal(object):
        def run(self):
            print('Animal is running...')
            
    当我们需要编写Dog和Cat类时,就可以直接从Animal类继承:
    class Dog(Animal):
        pass
    
    class Cat(Animal):
        pass
        
    对于Dog来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。
    继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,
    因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:
    dog = Dog()
    dog.run()
    
    cat = Cat()
    cat.run()
    
    运行结果如下:
    Animal is running...
    Animal is running...
    
    当然,也可以对子类增加一些方法,比如Dog类:
    class Dog(Animal):
    
        def run(self):
            print('Dog is running...')
    
        def eat(self):
            print('Eating meat...')
            
    继承的第二个好处需要我们对代码做一点改进。你看到了,无论是Dog还是Cat,它们run()的时候,显示的都是Animal is running...,
    符合逻辑的做法是分别显示Dog is running...和Cat is running...,因此,对Dog和Cat类改进如下:
    class Dog(Animal):
    
        def run(self):
            print('Dog is running...')
    
    class Cat(Animal):
    
        def run(self):
            print('Cat is running...')
            
    再次运行,结果如下:
    Dog is running...
    Cat is running...
    
    当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。
    这样,我们就获得了继承的另一个好处:多态。
    要理解什么是多态,我们首先要对数据类型再作一点说明。当我们定义一个class的时候,我们实际上就定义了一种数据类型。
    我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样:
    a = list() # a是list类型
    b = Animal() # b是Animal类型
    c = Dog() # c是Dog类型
    
    判断一个变量是否是某个类型可以用isinstance()判断:
    >>> isinstance(a, list)
    True
    >>> isinstance(b, Animal)
    True
    >>> isinstance(c, Dog)
    True
    
    看来a、b、c确实对应着list、Animal、Dog这3种类型。
    但是等等,试试:
    >>> isinstance(c, Animal)
    True
    
    看来c不仅仅是Dog,c还是Animal!
    不过仔细想想,这是有道理的,因为Dog是从Animal继承下来的,当我们创建了一个Dog的实例c时,
    我们认为c的数据类型是Dog没错,但c同时也是Animal也没错,Dog本来就是Animal的一种!
    
    所以,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:
    
    >>> b = Animal()
    >>> isinstance(b, Dog)
    False
    
    Dog可以看成Animal,但Animal不可以看成Dog。
    要理解多态的好处,我们还需要再编写一个函数,这个函数接受一个Animal类型的变量:
    def run_twice(animal):
        animal.run()
        animal.run()
    当我们传入Animal的实例时,run_twice()就打印出:
    
    >>> run_twice(Animal())
    Animal is running...
    Animal is running...
    当我们传入Dog的实例时,run_twice()就打印出:
    
    >>> run_twice(Dog())
    Dog is running...
    Dog is running...
    当我们传入Cat的实例时,run_twice()就打印出:
    
    >>> run_twice(Cat())
    Cat is running...
    Cat is running...
    看上去没啥意思,但是仔细想想,现在,如果我们再定义一个Tortoise类型,也从Animal派生:
    
    class Tortoise(Animal):
        def run(self):
            print('Tortoise is running slowly...')
    当我们调用run_twice()时,传入Tortoise的实例:
    
    >>> run_twice(Tortoise())
    Tortoise is running slowly...
    Tortoise is running slowly...
    
    你会发现,新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
    多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,
    因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,
    因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:
    
    对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,
    而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,
    这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。
    这就是著名的“开闭”原则:
    对扩展开放:允许新增Animal子类;
    对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
    
    
     
    面向对象之继承
    1.子类可以使用父类的所有属性和方法
    2.如果子类有自己的方法,就执行自己的;如果子类没有自己的方法,就会找父类的。
    
    3.如果子类里面没有找到,父类里也没有找到,就会报错
    
    4.如果子类中实现了调用父类的方法
    方法一:父类名.父类方法()
    class Vehicle: #定义交通工具类
         Country='China'
         def __init__(self,name,speed,load,power):
             self.name=name
             self.speed=speed
             self.load=load
             self.power=power
    
         def run(self):
             print('开动啦...')
    
    class Subway(Vehicle): #地铁
        def __init__(self,name,speed,load,power,line):
            Vehicle.__init__(self,name,speed,load,power)
            self.line=line
    
        def run(self):
            print('地铁%s号线欢迎您' %self.line)
            Vehicle.run(self)
    
    line13=Subway('中国地铁','180m/s','1000人/箱','',13)
    line13.run()
    
    方法二:super()
    class Vehicle: #定义交通工具类
         Country='China'
         def __init__(self,name,speed,load,power):
             self.name=name
             self.speed=speed
             self.load=load
             self.power=power
    
         def run(self):
             print('开动啦...')
    
    class Subway(Vehicle): #地铁
        def __init__(self,name,speed,load,power,line):
            super().__init__(name,speed,load,power)
            self.line=line
    
        def run(self):
            print('地铁%s号线欢迎您' %self.line)
            super(Subway,self).run()
    
    line13=Subway('中国地铁','180m/s','1000人/箱','',13)
    line13.run()
    
    在类内:super(子类,self).方法名()  supper().__init__(参数)
    在类外:super(子类名,对象名).方法名()
    
    
    5.多继承
    当类是经典类时,多继承情况下,会按照深度优先顺序去查找
    当类时新式类时,多继承情况下,会按照广度优先顺序去查找
    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        def test(self):
            print('from C')
    
    class D(B):
        def test(self):
            print('from D')
    
    class E(C):
        def test(self):
            print('from E')
    
    class F(D,E):
        # def test(self):
        #     print('from F')
        pass
    f1=F()
    f1.test()
    print (F.mro()) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    
    新式类继承顺序:F->D->B->E->C->A
    经典类继承顺序:F->D->B->A->E->C
    python3中统一都是新式类
    pyhon2中才分新式类与经典类
    
    新式类所有基类的线性顺序列表:
    F.mro() 
    
    
    
    python之反射
    python面向对象中的反射:通过字符串的形式操作对象相关的属性。
    python中的一切事物都是对象(都可以使用反射)
    
    hasattr(object,name)   判断object中有没有一个name字符串对应的方法或属性
    getattr(object, name, default=None)
    setattr(x, y, v) 
    delattr(x, y) 
    
    
    四个方法的使用演示
    class Foo:
        f = '类的静态变量'
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say_hi(self):
            print('hi,%s'%self.name)
    
    obj=Foo('egon',73)
    
    #检测是否含有某属性
    print(hasattr(obj,'name'))
    print(hasattr(obj,'say_hi'))
    
    #获取属性
    n=getattr(obj,'name')
    print(n)
    func=getattr(obj,'say_hi')
    func()
    
    print(getattr(obj,'aaaaaaaa','不存在啊')) #报错
    
    #设置属性
    setattr(obj,'sb',True)
    setattr(obj,'show_name',lambda self:self.name+'sb')
    print(obj.__dict__)
    print(obj.show_name(obj))
    
    #删除属性
    delattr(obj,'age')
    delattr(obj,'show_name')
    delattr(obj,'show_name111')#不存在,则报错
    
    print(obj.__dict__)
    
    反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’。
    可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
    范例:
    还没有实现全部功能
    class FtpClient:
        'ftp客户端,但是还么有实现具体的功能'
        def __init__(self,addr):
            print('正在连接服务器[%s]' %addr)
            self.addr=addr
      
    不影响下面代码的编写  
    #from module import FtpClient
    f1=FtpClient('192.168.1.1')
    if hasattr(f1,'get'):
        func_get=getattr(f1,'get')
        func_get()
    else:
        print('---->不存在此方法')
        print('处理其他的逻辑')
    
    
    使用__slots__
    正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。先定义class:
    class Student(object):
        pass
        
    然后,尝试给实例绑定一个属性:
    >>> s = Student()
    >>> s.name = 'Michael' # 动态给实例绑定一个属性
    >>> print(s.name)
    Michael
    
    还可以尝试给实例绑定一个方法:
    >>> 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
    
    但是,给一个实例绑定的方法,对另一个实例是不起作用的:
    >>> s2 = Student() # 创建新的实例
    >>> s2.set_age(25) # 尝试调用方法
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute 'set_age'
    
    为了给所有实例都绑定方法,可以给class绑定方法:
    
    >>> def set_score(self, score):
    ...     self.score = score
    ...
    >>> Student.set_score = set_score
    
    给class绑定方法后,所有实例均可调用:
    >>> s.set_score(100)
    >>> s.score
    100
    >>> s2.set_score(99)
    >>> s2.score
    99
    
    通常情况下,上面的set_score方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。
    但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。
    为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
        
    然后,我们试试:
    >>> s = Student() # 创建新的实例
    >>> s.name = 'Michael' # 绑定属性'name'
    >>> s.age = 25 # 绑定属性'age'
    >>> s.score = 99 # 绑定属性'score'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute 'score'
    
    由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
    使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
    >>> class GraduateStudent(Student):
    ...     pass
    ...
    >>> g = GraduateStudent()
    >>> g.score = 9999
    除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。
    
    
    
    使用@property
    在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改:
    
    s = Student()
    s.score = 9999
    
    这显然不合逻辑。为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,这样,在set_score()方法里,就可以检查参数:
    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
            
    现在,对任意的Student实例进行操作,就不能随心所欲地设置score了:
    >>> s = Student()
    >>> s.set_score(60) # ok!
    >>> s.get_score()
    60
    >>> s.set_score(9999)
    Traceback (most recent call last):
      ...
    ValueError: score must between 0 ~ 100!
    
    但是,上面的调用方法又略显复杂,没有直接用属性这么直接简单。
    
    有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?对于追求完美的Python程序员来说,这是必须要做到的!
    还记得装饰器(decorator)可以给函数动态加上功能吗?对于类的方法,装饰器一样起作用。
    Python内置的@property装饰器就是负责把一个方法变成属性调用的:
    class Student(object):
    
        @property
        def score(self):
            return self._score
    
        @score.setter
        def 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
            
    @property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,
    此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
    
    >>> s = Student()
    >>> s.score = 60 # OK,实际转化为s.set_score(60)
    >>> s.score # OK,实际转化为s.get_score()
    60
    >>> s.score = 9999
    Traceback (most recent call last):
      ...
    ValueError: score must between 0 ~ 100!
    
    注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。
    还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性:
    
    class Student(object):
    
        @property
        def birth(self):
            return self._birth
    
        @birth.setter
        def birth(self, value):
            self._birth = value
    
        @property
        def age(self):
            return 2015 - self._birth
            
    上面的birth是可读写属性,而age就是一个只读属性,因为age可以根据birth和当前时间计算出来。
    
    
    
    isinstance(obj,cls)和issubclass(sub,super)
    isinstance(obj,cls)检查是否obj是否是类 cls 的对象
    class Foo(object):
        pass
     
    obj = Foo()
     
    isinstance(obj, Foo)
    
    issubclass(sub, super)检查sub类是否是 super 类的派生类
    class Foo(object):
        pass
     
    class Bar(Foo):
        pass
    issubclass(Bar, Foo)
    
    
    
    __setattr__,__delattr__,__getattr__
    
    class Foo:
        x=1
        def __init__(self,y):
            self.y=y
    
        def __getattr__(self, item):
            print('----> from getattr:你找的属性不存在')
    
    
        def __setattr__(self, key, value):
            print('----> from setattr')
            # self.key=value #这就无限递归了 
            # self.__dict__[key]=value #应该使用它
    
        def __delattr__(self, item):
            print('----> from delattr')
            # del self.item #无限递归了
            self.__dict__.pop(item)
    
    #__setattr__添加/修改属性会触发它的执行
    f1=Foo(10)
    print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
    f1.z=3
    print(f1.__dict__)
    
    #__delattr__删除属性的时候会触发
    f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
    del f1.a
    print(f1.__dict__)
    
    #__getattr__只有在使用点调用属性且属性不存在的时候才会触发
    f1.xxxxxx
    
    
    
    __getattribute__
    
    当只有 __getattr__
    
    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __getattr__(self, item):
            print('执行的是我')
            # return self.__dict__[item]
    
    f1=Foo(10)
    print(f1.x)
    f1.xxxxxx #不存在的属性访问,触发__getattr__
    
    
    当只有 __getattribute__
    
    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __getattribute__(self, item):
            print('不管是否存在,我都会执行')
    
    f1=Foo(10)
    f1.x
    f1.xxxxxx
    
    
    当__getattr__ 和 __getattribute__ 同时出现
    
    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __getattr__(self, item):
            print('执行的是我')
            # return self.__dict__[item]
        def __getattribute__(self, item):
            print('不管是否存在,我都会执行')
            raise AttributeError('哈哈')
    
    f1=Foo(10)
    f1.x
    f1.xxxxxx
    
    #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
    
    
    
    __setitem__,__getitem,__delitem__
    
    class Foo:
        def __init__(self,name):
            self.name=name
    
        def __getitem__(self, item):
            print(self.__dict__[item])
    
        def __setitem__(self, key, value):
            self.__dict__[key]=value
        def __delitem__(self, key):
            print('del obj[key]时,我执行')
            self.__dict__.pop(key)
        def __delattr__(self, item):
            print('del obj.key时,我执行')
            self.__dict__.pop(item)
    
    f1=Foo('sb')
    f1['age']=18
    f1['age1']=19
    del f1.age1
    del f1['age']
    f1['name']='alex'
    print(f1.__dict__)
    
    
    
    __str__,__repr__,__format__
    改变对象的字符串显示__str__,__repr__
    自定制格式化字符串__format__
    format_dict={
        'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
        'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
        'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
    }
    class School:
        def __init__(self,name,addr,type):
            self.name=name
            self.addr=addr
            self.type=type
    
        def __repr__(self):
            return 'School(%s,%s)' %(self.name,self.addr)
        def __str__(self):
            return '(%s,%s)' %(self.name,self.addr)
    
        def __format__(self, format_spec):
            # if format_spec
            if not format_spec or format_spec not in format_dict:
                format_spec='nat'
            fmt=format_dict[format_spec]
            return fmt.format(obj=self)
    
    s1=School('oldboy1','北京','私立')
    print('from repr: ',repr(s1))
    print('from str: ',str(s1))
    print(s1)
    
    '''
    str函数或者print函数--->obj.__str__()
    repr或者交互式解释器--->obj.__repr__()
    如果__str__没有被定义,那么就会使用__repr__来代替输出
    注意:这俩方法的返回值必须是字符串,否则抛出异常
    '''
    print(format(s1,'nat'))
    print(format(s1,'tna'))
    print(format(s1,'tan'))
    print(format(s1,'asfdasdffd'))
    
    
    关于 __format__ 的范例
    
    date_dic={
        'ymd':'{0.year}:{0.month}:{0.day}',
        'dmy':'{0.day}/{0.month}/{0.year}',
        'mdy':'{0.month}-{0.day}-{0.year}',
    }
    class Date:
        def __init__(self,year,month,day):
            self.year=year
            self.month=month
            self.day=day
    
        def __format__(self, format_spec):
            if not format_spec or format_spec not in date_dic:
                format_spec='ymd'
            fmt=date_dic[format_spec]
            return fmt.format(self)
    
    d1=Date(2016,12,29)
    print(format(d1))
    print('{:mdy}'.format(d1))
    
    
    
    __call__
    对象后面加括号,触发执行。
    
    注:构造方法的执行是由创建对象触发的,即:对象 = 类名()
    而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
    
    class Foo:
    
        def __init__(self):
            pass
        
        def __call__(self, *args, **kwargs):
    
            print('__call__')
    
    
    obj = Foo() # 执行 __init__
    obj()       # 执行 __call__
    
    
    
    __module__和__class__
    
    __module__    表示当前操作的对象在那个模块
    __class__     表示当前操作的对象的类是什么
    
    lib/aa.py 文件
    class C:
    
        def __init__(self):
            self.name = 'from lib/aaa.py'
            
    另外的文件        
    from lib.aa import C
    
    obj = C()
    print obj.__module__  # 输出 lib.aa,即:输出模块
    print obj.__class__      # 输出 lib.aa.C,即:输出类
    
    
    
    类的描述信息 __doc__
    
    类的描述信息
    class Foo:
        '我是描述信息'
        pass
    
    class Bar(Foo):
        pass
    print(Bar.__doc__) #该属性无法继承给子类
    
    
    
    析构方法 __del__
    
    析构方法,当对象在内存中被释放时,自动触发执行。
    注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,
    即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__
    
    范例1.
    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    del f1
    print('------->')
    
    #输出结果
    执行我啦
    ------->
    
    
    范例2.
    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    # del f1
    print('------->')
    
    #输出结果
    ------->
    执行我啦
    
    
    典型的应用场景:
    
    创建数据库类,用该类实例化出数据库链接对象,对象本身是存放于用户空间内存中,而链接则是由操作系统管理的,存放于内核空间内存中
    当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,
    这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源
    这与文件处理是一个道理:
    
    f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件
    del f #只回收用户空间的f,操作系统的文件还处于打开状态
    
    #所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是
    f=open('a.txt')
    读写...
    f.close()
    很多情况下大家都容易忽略f.close,这就用到了with上下文管理
    
    
    
    __enter__和__exit__
    
    好处:
    1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
    2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处
    
    我们知道在操作文件对象的时候可以这么写
    with open('a.txt') as f:
      '代码块'
    
    上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法
    
    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
            
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        # print(f,f.name)
    
        
    __exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行
    
    
    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
    
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
    print('0'*100) #------------------------------->不会执行
    
    
    如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行
    
    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
            return True
    
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
    print('0'*100) #------------------------------->会执行
    
    
    
    __next__和__iter__实现迭代器协议
    
    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __iter__(self):
            return self
    
        def __next__(self):
            n=self.x
            self.x+=1
            return self.x
    
    f=Foo(3)
    for i in f:
        print(i)
        
        
    class Foo:
        def __init__(self,start,stop):
            self.num=start
            self.stop=stop
        def __iter__(self):
            return self
        def __next__(self):
            if self.num >= self.stop:
                raise StopIteration
            n=self.num
            self.num+=1
            return n
    
    f=Foo(1,5)
    from collections import Iterable,Iterator
    print(isinstance(f,Iterator))
    
    for i in Foo(1,5):
        print(i) 
        
        
        
    描述符(__get__,__set__,__delete__)
    
    1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
    __get__():调用一个属性时,触发
    __set__():为一个属性赋值时,触发
    __delete__():采用del删除属性时,触发
    
     
    
    class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
        def __get__(self, instance, owner):
            pass
        def __set__(self, instance, value):
            pass
        def __delete__(self, instance):
            pass
    
    
    2 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
    
    class Foo:
        def __get__(self, instance, owner):
            print('触发get')
        def __set__(self, instance, value):
            print('触发set')
        def __delete__(self, instance):
            print('触发delete')
    
    #包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
    f1=Foo()
    f1.name='egon'
    f1.name
    del f1.name
    #疑问:何时,何地,会触发这三个方法的执行
    
    
    
     
    
    
    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    #描述符Int
    class Int:
        def __get__(self, instance, owner):
            print('Int调用')
        def __set__(self, instance, value):
            print('Int设置...')
        def __delete__(self, instance):
            print('Int删除...')
    
    class People:
        name=Str()
        age=Int()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    #何地?:定义成另外一个类的类属性
    
    #何时?:且看下列演示
    
    p1=People('alex',18)
    
    #描述符Str的使用
    p1.name
    p1.name='egon'
    del p1.name
    
    #描述符Int的使用
    p1.age
    p1.age=18
    del p1.age
    
    #我们来瞅瞅到底发生了什么
    print(p1.__dict__)
    print(People.__dict__)
    
    #补充
    print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
    print(type(p1).__dict__ == People.__dict__)
    
    
    
    3 描述符分两种
    一 数据描述符:至少实现了__get__()和__set__()
    
    class Foo:
        def __set__(self, instance, value):
            print('set')
        def __get__(self, instance, owner):
            print('get')
    
    二 非数据描述符:没有实现__set__()
    
    class Foo:
        def __get__(self, instance, owner):
            print('get')
    
     
    
    4 注意事项:
    一 描述符本身应该定义成新式类,被代理的类也应该是新式类
    二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
    三 要严格遵循该优先级,优先级由高到底分别是
    1.类属性
    2.数据描述符
    3.实例属性
    4.非数据描述符
    5.找不到的属性触发__getattr__()
    
     
    
    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    
    #基于上面的演示,我们已经知道,在一个类中定义描述符它就是一个类属性,存在于类的属性字典中,而不是实例的属性字典
    
    #那既然描述符被定义成了一个类属性,直接通过类名也一定可以调用吧,没错
    People.name #恩,调用类属性name,本质就是在调用描述符Str,触发了__get__()
    
    People.name='egon' #那赋值呢,我去,并没有触发__set__()
    del People.name #赶紧试试del,我去,也没有触发__delete__()
    #结论:描述符对类没有作用-------->傻逼到家的结论
    
    '''
    原因:描述符在使用时被定义成另外一个类的类属性,因而类属性比二次加工的描述符伪装而来的类属性有更高的优先级
    People.name #恩,调用类属性name,找不到就去找描述符伪装的类属性name,触发了__get__()
    
    People.name='egon' #那赋值呢,直接赋值了一个类属性,它拥有更高的优先级,相当于覆盖了描述符,肯定不会触发描述符的__set__()
    del People.name #同上
    '''
    
    
    
    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    class People:
        name=Str()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    
    p1=People('egon',18)
    
    #如果描述符是一个数据描述符(即有__get__又有__set__),那么p1.name的调用与赋值都是触发描述符的操作,于p1本身无关了,相当于覆盖了实例的属性
    p1.name='egonnnnnn'
    p1.name
    print(p1.__dict__)#实例的属性字典中没有name,因为name是一个数据描述符,优先级高于实例属性,查看/赋值/删除都是跟描述符有关,与实例无关了
    del p1.name
    
    
    class Foo:
        def func(self):
            print('我胡汉三又回来了')
    f1=Foo()
    f1.func() #调用类的方法,也可以说是调用非数据描述符
    #函数是一个非数据描述符对象(一切皆对象么)
    print(dir(Foo.func))
    print(hasattr(Foo.func,'__set__'))
    print(hasattr(Foo.func,'__get__'))
    print(hasattr(Foo.func,'__delete__'))
    #有人可能会问,描述符不都是类么,函数怎么算也应该是一个对象啊,怎么就是描述符了
    #笨蛋哥,描述符是类没问题,描述符在应用的时候不都是实例化成一个类属性么
    #函数就是一个由非描述符类实例化得到的对象
    #没错,字符串也一样
    
    
    f1.func='这是实例属性啊'
    print(f1.func)
    
    del f1.func #删掉了非数据
    f1.func()
    
    
    class Foo:
        def __set__(self, instance, value):
            print('set')
        def __get__(self, instance, owner):
            print('get')
    class Room:
        name=Foo()
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
    
    #name是一个数据描述符,因为name=Foo()而Foo实现了get和set方法,因而比实例属性有更高的优先级
    #对实例的属性操作,触发的都是描述符的
    r1=Room('厕所',1,1)
    r1.name
    r1.name='厨房'
    
    
    class Foo:
        def __get__(self, instance, owner):
            print('get')
    class Room:
        name=Foo()
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
    
    #name是一个非数据描述符,因为name=Foo()而Foo没有实现set方法,因而比实例属性有更低的优先级
    #对实例的属性操作,触发的都是实例自己的
    r1=Room('厕所',1,1)
    r1.name
    r1.name='厨房'
    
    
    
    class Foo:
        def func(self):
            print('我胡汉三又回来了')
    
        def __getattr__(self, item):
            print('找不到了当然是来找我啦',item)
    f1=Foo()
    
    f1.xxxxxxxxxxx
    
    
    
    5 描述符使用
    
    众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能
    
    class Str:
        def __init__(self,name):
            self.name=name
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name')
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People('egon',18,3231.3)
    
    #调用
    print(p1.__dict__)
    p1.name
    
    #赋值
    print(p1.__dict__)
    p1.name='egonlin'
    print(p1.__dict__)
    
    #删除
    print(p1.__dict__)
    del p1.name
    print(p1.__dict__)
    
    
    class Str:
        def __init__(self,name):
            self.name=name
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name')
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    #疑问:如果我用类名去操作属性呢
    People.name #报错,错误的根源在于类去操作属性时,会把None传给instance
    
    #修订__get__方法
    class Str:
        def __init__(self,name):
            self.name=name
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name')
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    print(People.name) #完美,解决
    
    
    
    class Str:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type): #如果不是期望的类型,则抛出异常
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Str('name',str) #新增类型限制str
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常
    
    
    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name=Typed('name',str)
        age=Typed('name',int)
        salary=Typed('name',float)
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People(123,18,3333.3)
    p1=People('egon','18',3333.3)
    p1=People('egon',18,3333)
    
    
    
    大刀阔斧之后我们已然能实现功能了,但是问题是,如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现,low,这时候我需要教你一招:独孤九剑
    
    
    def decorate(cls):
        print('类的装饰器开始运行啦------>')
        return cls
    
    @decorate #无参:People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People('egon',18,3333.3)
    
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            return cls
        return decorate
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    p1=People('egon',18,3333.3)
    
    
    终极大招
     
    
    class Typed:
        def __init__(self,name,expected_type):
            self.name=name
            self.expected_type=expected_type
        def __get__(self, instance, owner):
            print('get--->',instance,owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->',instance,value)
            if not isinstance(value,self.expected_type):
                raise TypeError('Expected %s' %str(self.expected_type))
            instance.__dict__[self.name]=value
        def __delete__(self, instance):
            print('delete--->',instance)
            instance.__dict__.pop(self.name)
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>',kwargs)
            for name,expected_type in kwargs.items():
                setattr(cls,name,Typed(name,expected_type))
            return cls
        return decorate
    @typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self,name,age,salary):
            self.name=name
            self.age=age
            self.salary=salary
    
    print(People.__dict__)
    p1=People('egon',18,3333.3)
    
    
    
    6 描述符总结
    
    描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
    
    描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
    
    7 利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)
    
    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @property
        def area(self):
            return self.width * self.length
    
    r1=Room('alex',1,1)
    print(r1.area)
    
    
    
    class Lazyproperty:
        def __init__(self,func):
            self.func=func
        def __get__(self, instance, owner):
            print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
            if instance is None:
                return self
            return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情
    
    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
        def area(self):
            return self.width * self.length
    
    r1=Room('alex',1,1)
    print(r1.area)
    
    
    
    class Lazyproperty:
        def __init__(self,func):
            self.func=func
        def __get__(self, instance, owner):
            print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
            if instance is None:
                return self
            else:
                print('--->')
                value=self.func(instance)
                setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
                return value
    
    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
        def area(self):
            return self.width * self.length
    
    r1=Room('alex',1,1)
    print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
    print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
    
    
    
    
    #缓存不起来了
    
    class Lazyproperty:
        def __init__(self,func):
            self.func=func
        def __get__(self, instance, owner):
            print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
            if instance is None:
                return self
            else:
                value=self.func(instance)
                instance.__dict__[self.func.__name__]=value
                return value
            # return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情
        def __set__(self, instance, value):
            print('hahahahahah')
    
    class Room:
        def __init__(self,name,width,length):
            self.name=name
            self.width=width
            self.length=length
    
        @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
        def area(self):
            return self.width * self.length
    
    print(Room.__dict__)
    r1=Room('alex',1,1)
    print(r1.area)
    print(r1.area) 
    print(r1.area) 
    print(r1.area) #缓存功能失效,每次都去找描述符了,为何,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了
    
    
     
    8 利用描述符原理完成一个自定制@classmethod
    
    class ClassMethod:
        def __init__(self,func):
            self.func=func
    
        def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
            def feedback():
                print('在这里可以加功能啊...')
                return self.func(owner)
            return feedback
    
    class People:
        name='linhaifeng'
        @ClassMethod # say_hi=ClassMethod(say_hi)
        def say_hi(cls):
            print('你好啊,帅哥 %s' %cls.name)
    
    People.say_hi()
    
    p1=People()
    p1.say_hi()
    #疑问,类方法如果有参数呢,好说,好说
    
    class ClassMethod:
        def __init__(self,func):
            self.func=func
    
        def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
            def feedback(*args,**kwargs):
                print('在这里可以加功能啊...')
                return self.func(owner,*args,**kwargs)
            return feedback
    
    class People:
        name='linhaifeng'
        @ClassMethod # say_hi=ClassMethod(say_hi)
        def say_hi(cls,msg):
            print('你好啊,帅哥 %s %s' %(cls.name,msg))
    
    People.say_hi('你是那偷心的贼')
    
    p1=People()
    p1.say_hi('你是那偷心的贼')
    
    
    
    9 利用描述符原理完成一个自定制的@staticmethod
    
    class StaticMethod:
        def __init__(self,func):
            self.func=func
    
        def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
            def feedback(*args,**kwargs):
                print('在这里可以加功能啊...')
                return self.func(*args,**kwargs)
            return feedback
    
    class People:
        @StaticMethod# say_hi=StaticMethod(say_hi)
        def say_hi(x,y,z):
            print('------>',x,y,z)
    
    People.say_hi(1,2,3)
    
    p1=People()
    p1.say_hi(4,5,6)
    
    
    
    再看property
    
    一个静态属性property本质就是实现了get,set,delete三种方法
    
    class Foo:
        @property
        def AAA(self):
            print('get的时候运行我啊')
    
        @AAA.setter
        def AAA(self,value):
            print('set的时候运行我啊')
    
        @AAA.deleter
        def AAA(self):
            print('delete的时候运行我啊')
    
    #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
    f1=Foo()
    f1.AAA
    f1.AAA='aaa'
    del f1.AAA
    
    
     
    
    class Foo:
        def get_AAA(self):
            print('get的时候运行我啊')
    
        def set_AAA(self,value):
            print('set的时候运行我啊')
    
        def delete_AAA(self):
            print('delete的时候运行我啊')
        AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应
    
    f1=Foo()
    f1.AAA
    f1.AAA='aaa'
    del f1.AAA
    
     
    怎么用?
    
     
    class Goods:
    
        def __init__(self):
            # 原价
            self.original_price = 100
            # 折扣
            self.discount = 0.8
    
        @property
        def price(self):
            # 实际价格 = 原价 * 折扣
            new_price = self.original_price * self.discount
            return new_price
    
        @price.setter
        def price(self, value):
            self.original_price = value
    
        @price.deleter
        def price(self):
            del self.original_price
    
    
    obj = Goods()
    obj.price         # 获取商品价格
    obj.price = 200   # 修改商品原价
    print(obj.price)
    del obj.price     # 删除商品原价
    
     
    #实现类型检测功能
    
    #第一关:
    class People:
        def __init__(self,name):
            self.name=name
    
        @property
        def name(self):
            return self.name
    
    # p1=People('alex') #property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这面写会触发property内置的set,抛出异常
    
    
    #第二关:修订版
    
    class People:
        def __init__(self,name):
            self.name=name #实例化就触发property
    
        @property
        def name(self):
            # return self.name #无限递归
            print('get------>')
            return self.DouNiWan
    
        @name.setter
        def name(self,value):
            print('set------>')
            self.DouNiWan=value
    
        @name.deleter
        def name(self):
            print('delete------>')
            del self.DouNiWan
    
    p1=People('alex') #self.name实际是存放到self.DouNiWan里
    print(p1.name)
    print(p1.name)
    print(p1.name)
    print(p1.__dict__)
    
    p1.name='egon'
    print(p1.__dict__)
    
    del p1.name
    print(p1.__dict__)
    
    
    #第三关:加上类型检查
    class People:
        def __init__(self,name):
            self.name=name #实例化就触发property
    
        @property
        def name(self):
            # return self.name #无限递归
            print('get------>')
            return self.DouNiWan
    
        @name.setter
        def name(self,value):
            print('set------>')
            if not isinstance(value,str):
                raise TypeError('必须是字符串类型')
            self.DouNiWan=value
    
        @name.deleter
        def name(self):
            print('delete------>')
            del self.DouNiWan
    
    p1=People('alex') #self.name实际是存放到self.DouNiWan里
    p1.name=1
  • 相关阅读:
    [转]Oracle创建删除用户、角色、表空间、导入导出数据库命令行方式总结
    [转]23种设计模式与泡MM的关系
    [转]23种设计模式之间的关系
    [转]如何提高服务器的访问速度
    SVN所在的服务器IP改变了,肿么办
    HTML中ID与NAME的区别
    Java与.net异构平台上web service间复杂对象的互操作
    下一代OS系统展望之我见(针对windows,其他OS我不熟)
    使用axis开发java web service
    关于Java与DotNet异构平台WebService中enum对象的交互
  • 原文地址:https://www.cnblogs.com/l10n/p/13932367.html
Copyright © 2011-2022 走看看