【Python3 笔记】Python3 面向对象 高级编程 __slots__ @property 多重继承 定制类 枚举类

本系列是学习 廖雪峰 Python3 教程 过程中记录的笔记,本篇文章记录 Python 中面向对象的高级编程,主要内容有 __slots__(限制属性)、@property(简化代码)、多重继承、定制类里面的一些小组件、枚举类和元类。

面向对象 高级编程

使用 __slots__

  • 用途:限制实例的属性,可以控制有哪些属性;

  • 可以给实例绑定任何属性和方法,但是仅对本实例有效;

  • 可以给 class 绑定任何属性和方法,对该类的所有实例都有效;

  • 使用 __slots__ 可以限制实例的属性,用 tuple 定义允许绑定的属性名称,这种方法仅对本类的实例起作用,对继承的子类的实例不起作用;

    class Student(object):
        pass
    
    def set_score(self, score):
        self.score = score
    
    from types import MethodType
    Student.set_score = MethodType(set_score, Student)  # 绑定类方法
    Student.school = "uestc"    # 绑定类属性
    s = Student()
    s.set_score(score=30)       # 实例1修改类属性的值
    print(s.score, s.school)
    
    s_2 = Student()             # 该类的第二个实例
    print(s_2.score, s_2.school)
    
    s_2.set_score(score=50)     # 实例2修改类属性的值
    print(s.score)
    
    class Student_s(object):
        __slots__ = ('school', 'age')  # 用tuple定义允许绑定的属性名称
    
    class GraduateStudent(Student_s):
        __slots__ = ('score')
        pass
    
    s_s = Student_s()
    # s_s.score = 90            # 报错,该类允许的属性中没有 score
    g_s = GraduateStudent()
    g_s.score = 100             # 可运行,子类属性中添加了 score
    print(g_s.score)
    # g_s.name = "morton"       # 报错,该类及其父类允许的属性中没有 name
    
    output——————————————————
    30 uestc
    30 uestc
    50
    100
    

使用 @property

  • 用途:@property 广泛应用在类的定义中,简化代码,同时保证对参数进行必要的检查;

  • 使用 @mothod.setter 时,同名的 mothod 一定要有 @property 修饰;

  • 【注】一下代码中的 _xxx 表示这是私有变量,去掉下划线,在赋值的时候会出现递归溢出;

    # 请利用@property给一个Screen对象加上 width
    # 和 height属性,以及一个只读属性resolution:
    class Screen(object):
        @property                   # width 可读
        def width(self):
            return self._width
    
        @property                   # height 可读
        def height(self):
            return self._height
    
        @property                   # resolution 可读
        def resolution(self):
            return self._width * self._height
    
        @width.setter                # width 可写
        def width(self, value):
            print('width 此处进行参数检查······')
            self._width = value
    
        @height.setter               # height 可写
        def height(self, value):
            print('height 此处进行参数检查······')
            self._height = value
    
    # 测试:
    scr = Screen()
    scr.width = 1024
    scr.height = 768
    print('width =', scr.width, 'height =', scr.height, 'resolution =', scr.resolution)
    if scr.resolution == 786432:
        print('测试通过!')
    else:
        print('测试失败!')
    
    output————————————————
    width 此处进行参数检查······
    height 此处进行参数检查······
    width = 1024 height = 768 resolution = 786432
    测试通过!
    

多重继承

  • 用途:通过多重继承,子类可以同时获得多个父类的所有功能;
  • MixIn 设计的目的是给一个类增加多个功能,与组合类似,优先考虑通过多重继承来组合实现多个 MixIn 的功能,而非设计层级复杂的继承关系;
  • Python 允许多重继承,Java 只允许单一继承;

定制类

  • 用途:为类添加一些小功能,类似于自定义打印对象的输出、使对象具有函数的调用性质等;

  • 仔细查看以下示例和解析,涉及到的定制类都有具体用法:

    class Chain(object):
        count = 0
    
        def __init__(self, path=''):    # 初始化,一旦定义新的实例就会执行
            self.__path = path
            Chain.count += 1            # 记录初始化的次数
            print("count = ", Chain.count)
    
        def __getattr__(self, path):    # 动态返回一个属性,需要的属性未定义时自动调用
            print("path : ", path)
            return Chain('%s/%s' % (self.__path, path))
    
        def __call__(self, name):       # 使对象Callable,可以像普通函数一样调用
            print("name : ", name)
            return Chain('%s/%s' % (self.__path, name))
    
        def __str__(self):              # 自定义打印对象时的输出
            print("这里可以自定义,输出你喜欢的东西")
            return self.__path
        __repr__ = __str__              # 自定义打印对象时的输出,为调试服务
    
    print(Chain().users('michael').repos)
    
    output————————————————
    count =  1
    path :  users
    count =  2
    name :  michael
    count =  3
    path :  repos
    count =  4
    这里可以自定义,输出你喜欢的东西
    /users/michael/repos
    
  • 【解析】

    Step 1:
    Chain()  # 实例化
    
    Step 2:
    Chain().users  # 由于没有给实例传入初始化对应的users属性的具体信息,从而自动调用__getattr__()函数,从而有:
    Chain().users = Chain('\users') # 这是重建实例,新实例的属性 new.__path = \users;
    
    Step 3:
    Chain().users('michael')  # 由于定义了__call__()函数,该实例可以直接像普通函数一样调用,michael 是函数参数,从而有:
    Chain().users('michael') = Chain('\users')('michael') = Chain('\users\michael')
    # 【关键】再一次重建实例,覆盖掉 Chain('\users'),新实例的属性 renew.__path = \users\michael;
    
    Step 4:
    Chain().users('michael').repos
    # 这一步是查询renew实例的属性repos,由于没有这一属性,就会执行__getattr__()函数,再一次返回新的实例Chain('\users\michael\repos')并且覆盖点之前的实例,
    # 这里记 trinew =Chain('\users\michael\repos'),不要忘了,一旦定义了一个新的实例,就会执行__init__方法;
    
    Step 5:
    print(Chain().users('michael').repos) = print(trinew)  # 由于定义了__str__() 方法,那么打印的时候就会调用此方法,据此方法的定义,打印回来的是trinew的__path属性,即 \users\michael\repos
    
    # 至此,我们也把所有定义的有特殊用的方法都用上了,完毕。
    

枚举类

  • 用途:固定的可以列举的对象;

  • from enum import Enum, unique

  • Enum: 通过继承 Enum 类可以实现枚举类 ;unique: 避免出现重复的枚举类型(value相同);

  • 若没有使用装饰器 @unique,而枚举类中又出现 value相同的枚举类型,在遍历的时候后面的成员会被忽略,而要能遍历到重复的成员,需要使用一个特殊的属性 __members__ ,它包含了枚举类中的所有成员。

    from enum import Enum, unique
    
    Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                           'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
    
    @unique
    class Weekday(Enum):
        Sun = 0      # Sun的value被设定为0
        Mon = 1
        Tue = 2
        Wed = 3
        Thu = 4
        Fri = 5
        Sat = 6
    
    for name, member in Month.__members__.items():
        print(name, '=>', member, ',', member.value)
    
    for name, member in Weekday.__members__.items():
        print(name, '=>', member, ',', member.value)
    
    print("访问 Mon 的几种方法:")
    print("1.  ", Weekday.Mon)
    print("2.  ", Weekday['Mon'])
    print("3.  ", Weekday(1))
    
    output——————————————
    Jan => Month.Jan , 1
    Feb => Month.Feb , 2
    Mar => Month.Mar , 3
    Apr => Month.Apr , 4
    May => Month.May , 5
    Jun => Month.Jun , 6
    Jul => Month.Jul , 7
    Aug => Month.Aug , 8
    Sep => Month.Sep , 9
    Oct => Month.Oct , 10
    Nov => Month.Nov , 11
    Dec => Month.Dec , 12
    Sun => Weekday.Sun , 0
    Mon => Weekday.Mon , 1
    Tue => Weekday.Tue , 2
    Wed => Weekday.Wed , 3
    Thu => Weekday.Thu , 4
    Fri => Weekday.Fri , 5
    Sat => Weekday.Sat , 6
    访问 Mon 的几种方法:
    1.   Weekday.Mon
    2.   Weekday.Mon
    3.   Weekday.Mon
    

使用元类(metaclass

  • 用途:type() 可以动态创建类, metaclass 可以控制类的创建行为,例如:动态增加类的方法(使用需谨慎,一般也用不到);

  • type() 函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过 type()函数创建出 Hello 类,而无需通过 class Hello(object)... 的定义:

  • 要创建一个class对象,type() 函数依次传入3个参数:

    1. class 的名称;
    2. 继承的父类集合,注意 Python 支持多重继承,如果只有一个父类,别忘了 tuple 的单元素写法;
    3. class 的方法名称与函数绑定,这里我们把函数 fn 绑定到方法名 hello 上。
    def fn(self, name='world'):  # 先定义函数
        print('Hello, %s.' % name)
    
    Hello = type('Hello', (object,), dict(hello=fn))  # 创建Hello class
    h = Hello()
    h.hello()
    
    output————————————
    Hello, world.
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值