python 面向对象高级编程

使用__slots__

参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017501655757856

  slots 是跟踪的意思,在python中是在类中使用一个变量,主要的功能是 限制 为该类的实例绑定的属性 只能是__slots__中声明的

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

  但是这个只对当前类(Student)的实例起作用,不对子类起作用,如果子类中也有__slots__这个变量,那么可以为子类实例绑定的属性就是   父类和子类__slots__的并集

使用@property

  参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017502538658208

  这是个装饰器,功能是能够让 封装数据的方法 像实例的属性一样使用,为什么增加能封装数据的方法呢,因为可以对设置实例的属性时可以检查,但是实例调用方法不如通过属性来访问那样简便,虽然都是一行代码

  这个是python原生实现的装饰器,是为了让方法变成属性调用

  怎么使用呢,和装饰器的方法一模一样

  

  @property装饰器是将读取属性的方法变成属性调用的 。然后@property装饰器又创建了另外一个装饰器@score.setter,这个装饰器是为了   把设置属性的方法  变成  属性 调用的

  注意一个@score.setter可以写道多个属性的设置方法上,

class Student(object):
    count = 0

    @property#下面是getter
    def score(self):
        return self.__score

    @score.setter#下面是#setter,注意这个下面函数名就是设置时调用的名称,可以和getter的函数名相同,之所以不同仅仅是为了说明,不同时也可以工作
    def set_score(self,value):
        if not isinstance(value,int):
            raise ValueError('value must int object!')
        elif not 0<=value<=100:
            raise ValueError('value must between 0 and 100')
        else:
            self.__score=value

    @property#如果一个属性只用@property包裹,那么这个属性是只读的,设置时会出错
    def difference(self):
        return 100-self.score

  @property 广泛运用在类的定义中,可以让使用者写出简短的代码,同时又能保证了对参数的检查,避免程序出现意外错误

class Screen(object):
    @property
    def width(self):
        return self.__width
    @width.setter
    def width(self,value):
        self.__width=value 
    
    @property
    def height(self):
        return self.__height
    @width.setter里面的s.height出来的一直是1024,我一直不知道为什么,最后才发现是这里写错了,因为是复制过来的然后没改完,总是犯这样的错误,而且最后错误出现的时候也不会溯源
    def height(self,value):
        self.__height=value

    @property
    def resolution(self):
        return self.width*self.height#还要注意上面的错误可以在这里将width改为__width,__height来解决

if __name__ == "__main__":
    s = Screen()
    s.height = 768
    s.width = 1024

    print('resolution =', s.resolution)
    if s.resolution == 786432:
        print('测试通过!')
    else:
        print('测试失败!')
    pass

多重继承MaxIn

  参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017502939956896

  python 允许多重继承,而有的语言只允许单一集成(Java)

  在面向对象的设计思想中,通常按照不同的归属关系来设置类间的层次,但是归类关系的不同会造成多种多样的层次,会大大增加层次的复杂度,这样的设计是不行的。

  采用多重继承,可以让一个类同时继承多个类,从而拥有多个父类的功能

  为了使条例清晰,就指定主要的层次和

  在设计类的关系时,通常都是单一继承下来的,如果需要增加额外的类,就通过多重继承来实现,这种设计通常称为MaxIn

  为了更好的看出类的继承关系,我们就把额外的类增加一个MaxIn后缀名,来表示他不是主要的类

  MaxIn的目的就是为一个类增加多重功能,从而只设计类的时候优先考虑通过多重继承的方式来组合多个MaxIn的功能,而不是设计复杂的层次关系,只需要根据需要选择不同的功能的类来组合,就可以快速构建出所需要的类

 

定制类

  参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017590712115904

  我们在定义类的时候,除了寻常的定义类的功能之外,还能通过在类内部定义一些特殊的函数如__init__等来让类有一些另外的功能,这些函数是用来定义特殊功能的

  __str__()和__repr__()

  修改打印实例时显示的字符

  

>>> print(Student('Michael'))
<__main__.Student object at 0x109afb190>

  打印出一堆<__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)

  但是直接敲出来变量打印出来的还时原来的样子

>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>

  这是因为显示变量用的是__repr__()而不是__str__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

  解决办法是再定义一个__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__

  __iter__()

  前面已经说过、我们定义的类其实也是一种数据类型,可以使用isinstance()来判断,如果我们想要让定义的类能够被迭代,即传入for循环中,我们就可以通过定义__iter__()这个特殊函数来实现

  该方法返回一个迭代对象,然后for循环就会不断的调用迭代对象的__next__()来获取下一个值,直到遇到StopIterable错误结束循环

  作者举了一个例子,以斐波那契数列为例,定义了一个Fib类,可以作用于for循环

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b

    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

 

  __getitem__

  如果我们想让定义的类像list数据类型一样,可以通过下标来访问,就可以通过定义__getitem__()来实现

class Fib(object):#其实是通过重新执行  生成循环 然后在要取的那个值让循环结束,返回这个值,不是从生成好的结果中取,因为他不是用来储存的数据类型
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a

  但是这里还不能实现List的切片功能,如果想要实现,仍然可以

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

  如果想要对切片的step参数和负数也做相应的实现,作者说也可以

  如果把对象看成dict__getitem__()的参数也可能是一个可以作key的object,例如str

  与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

  总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

  __getattr__

  通过定义这个函数,我们可以在使用实例访问未定义的属性时,返回这个属性,原理就是在这个函数中创建这个属性然后返回、即动态的返回一个属性

  当调用未存在的属性时,Python会调用__getattr__来试图获得一个属性。注意,只有在调用的属性不存在时,才会去这个函数中寻找

class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99

  此外,也可以返回一个函数

class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25

  不过相应的调用方式也需要改变,因为调用的方法嘛

>>> s.age()
25

  到此为止我们就实现了可以访问没有定义的属性,但是我们现在访问任何属性都可以,如果我们没有在__getattr__中对这个属性做处理,就会返回None,如果我们想要只响应几个特定的属性,我们就需要让__getattr__还能抛出异常

class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

  作者说这个方法的用处很大,可以把一个类的所有属性和方法的调用全部动态化处理,不需要任何特殊手段了。

  作者举了一个例子说明了动态化处理的应用,针对一些完全动态化的情况

  比如实现一个根据URL实现完全动态调用的SDK,

class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):#
        return Chain('%s/%s' % (self._path, path))#这里为什么又用China创建了实例?

    def __str__(self):
        return self._path

    __repr__ = __str__

#效果
>>> Chain().status.user.timeline.list#请求的时status.user.timeline.list属性#这样就是链式调用
'/status/user/timeline/list'#返回的

  对应于在SDK中为每一个URL的API写一个方法,通过__getattr__()来实现不仅简单,而且还不用随着API的改变而改变

  __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.

  __call__还可以定义参数,这样的话,调用对象就和调用函数一样了

  所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

  如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

  怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例:

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False

  通过callable()可以判断一个对象是否时可调用对象

  

使用枚举类

  参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017595944503424

  对象实例有限且固定的 类称为 枚举类,比如季节、月份等

  python中提供了Enum类

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

  这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:

for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

  value属性则是自动赋给成员的int常量,默认从1开始计数。

  如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:

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

  @unique装饰器可以帮助我们检查保证没有重复值。

  可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。

  Enum可以把一组相关常量定义在一个class中,且class不可变,而且成员可以直接比较。

理解元类、ORM

  参考链接:https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072

  笔记(其实都是抄的):https://www.cnblogs.com/Gaoqiking/p/10744253.html

  

  

猜你喜欢

转载自www.cnblogs.com/Gaoqiking/p/11575325.html