• Python基础(十二)—面向对象拾遗(__slots__、@property、枚举类、元类)


    编译型语言和解释型语言

    • 编译型语言
      • 定义
        需通过编译器(compiler)将源代码编译成机器码,之后才能执行的语言。一般需经过编译(compile)、链接(linker)这两个步骤。
        • 编译是把源代码编译成机器码
        • 链接是把各个模块的机器码和依赖库串连起来生成可执行文件
      • 优缺点
        • 优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。
        • 缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
      • 代表语言
        C、C++、Pascal、Object-C以及最近很火的苹果新语言swift
    • 解释型语言
      • 定义
        解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。
      • 优缺点:
        • 优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护。
        • 缺点:每次运行的时候都要解释一遍,性能上不如编译型语言。
      • 代表语言
        JavaScript、Python、Erlang、PHP、Perl、Ruby

    动态语言和静态语言

    • 静态类型语言
      • 定义
        类型判断是在运行前判断(如编译阶段),比如C#、java就是静态类型语言,静态类型语言为了达到多态会采取一些类型鉴别手段,如继承、接口
      • 优缺点
        优点:结构非常规范,便于调试,方便类型安全;缺点:为此需要写更多的类型相关代码,导致不便于阅读、不清晰明了。
    • 动态语言
      • 定义
        程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化,类型的检查是在运行时做的。
      • 优缺点
        优点:方便阅读,不需要写非常多的类型相关的代码;缺点:不方便调试,命名不规范时会造成读不懂,不利于理解等。
    • 关于弱类型、强类型、动态类型、静态类型语言的划分
      类型划分.png

    使用__slots__

    限制实例的属性

    • 动态语言动态绑定属性和方法
      当定义了一个class,创建实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性,绑定方式及效果请看以下代码

    对实例绑定的属性或方法只对绑定实例有效,对另外实例无效。采用给class绑定,则对所有实例均有效。

    class Student:
        pass
    
    s1 = Student()
    s1.name = 'lz'
    print(s1.name)  # lz
    
    s2 = Student()
    # print(s2.name)  # 报错:AttributeError: 'Student' object has no attribute 'name'
    
    def set_age(self, age):
        self.age = age
    from types import MethodType
    s1.set_age = MethodType(set_age, s1)
    s1.set_age(17)
    print(s1.age)  # 17,对s2不起作用
    
    # 对实例绑定的属性或方法只对绑定实例有效,对另外实例无效。采用给class绑定,则对所有实例均有效。
    Student.set_age = MethodType(set_age ,Student)
    s3 = Student()
    s3.set_age(20)
    print(s3.age)  # 20
    
    
    • 使用__slots__限制实例的属性
      如果想要限定实例的属性,可以使用__slots__对类属性进行限制,但是只对当前类的实例有效,对其子类无效。
    class Fish:
        __slots__ = ('name', 'age', 'sex')  # 如果这里没有sex,则下面的print会报错没有该属性
    
        def __init__(self, sex):
            self.sex = sex
    
    f1 = Fish('man')
    print(f1.sex)
    
    # f1.size = 18  # 报错没有该属性
    
    

    @property

    @property广泛应用于类的定义中,可以简化代码,同时保证对参数进行必要的检查,检查程序运行的出错。

    在Python基础(十)—面向对象的深入讲解(继承、Mixin编程机制等)章节中,说的是property(fget=None, fset=None, fdel=None, doc=None):通过类定义的属性设置属性。和@property有本质区别.

    • 范围限定
      例如我们新建Student,在设置成绩sorce属性上,我们是要对sorce进行范围限定的,这个就需要在class中的set方法中进行相对的限定,如下代码:
    class Student:
        def get_sorce(self):
            return self.sorce
        def set_sorce(self, value):
            if not isinstance(value, int):
                raise ValueError('sorce必须是Int')
            if value < 0 or value > 100:
                raise ValueError('sorce必须介于0~100')
            self._sorce = value
    

    单下划线和双下划线都是限定属性的私有

    • 使用@property装饰器讲方法变为属性
      @property可以使方法变为属性的方式进行访问,以上代码修改为:
    class Student:
      @property
      def sorce(self):
          return self.sorce
    
      @sorce.setter
      def set_sorce(self, value):
          if not isinstance(value, int):
              raise ValueError('sorce必须是Int')
          if value < 0 or value > 100:
              raise ValueError('sorce必须介于0~100')
          self._sorce = value
    #把一个getter方法加上@property,就变成了属性,同时@property本身又创建了另一个装饰器@sorce.setter,负责把一个setter方法变成属性赋值,这样就拥有了一个可控的属性操作。
    
    • 利用@property进行属性的读写限制
      如果之定义getter方法,而不定义setter方法,那该属性则是只读的,从以下方法可看出,age不需要可写,只需当前时间减去birth即可,因此设置为只读。
    class Man:
      @property
      def birth(self):
          return self._birth
      @birth.setter
      def birth(self, value):
          self._birth = value
      
      @property
      def age(self):
          return 2019-self._birth
      }
    

    使用枚举类

    • 使用枚举类的好处
      对于一些常量(星期,月份等),使用不可变的枚举类,即增加了可读性,也不用担心出错,并且枚举类在内存中存放的是数字,也提高了程序效率,同时成员还可以直接比较。
    • 创建枚举类
      枚举类型可以通过继承Enum类来实现,注意Enum类是在enum模块中的。
    from  enum import Enum
    class VIP(Enum):
        YELLOW = 1
        RED = 2
        BLUE = 3
        ```
      * 查看枚举类型
        枚举类型是一个特殊的类,我们可以查看它的名称和值。
    ```javascript
    print(VIP.YELLOW) #枚举类型
    print(VIP['YELLOW']) #枚举类型
    print(VIP.YELLOW.name) #枚举名称
    print(VIP.YELLOW.value)  #枚举值
    print(VIP(3)) #数字得到枚举类型
    
    #遍历
    for v in VIP:  #遍历
        print(v)
    
    # 成员的value默认从1开始
    for name, member in VIP.__members__.items():
        print(name, "->", member, ',', member.value)
    """
    YELLOW -> VIP.YELLOW , 1
    RED -> VIP.RED , 2
    BLUE -> VIP.BLUE , 3
    """
    
    • 重复和唯一的枚举类型
      当存在枚举成员的名称有重复时,则后面的枚举成员相当于第一个枚举成员的别名,而且在实际使用中,就是使用的第一次出现的枚举成员。
    from enum import Enum, unique
    @unique
    class Mistake(Enum):
        ONE = 1
        TWO = 2
        THREE = 3
        FOUR = 3
    """
    出错:ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE
    """
    
    • 枚举类型的比较
      枚举类型不能做大小比较,但是可以做身份比较和等值比较。枚举类型没有定义比较运算符,通常不能进行大小比较(不过,继承“IntEnum"类的枚举类型可以进行大小比较,他们的枚举值只能是整数)。

    枚举类型是使用单例模式实现的。在创建枚举类的时候,Python就在内存中为我们创建了枚举类的对象,因此我们不必实例化枚举类。并且由于枚举类的“new”方法,将会保证内存中只会存在一个枚举类的实例。

    type()创建类

    动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是在运行时动态创建的。

    • 假如我们定义了Hello的class,并保存为hello.py.当Python解释器载入hello.py模块时,会依次执行该模块的所有语句,执行的结果就是动态地创建出一个hello的class对象,测试如下(type()可以查看一个类型或变量的类型):
    class Hello(object):
        def hello(self, name='world'):
            print('hello %s.' % name)
    
    h = Hello()
    print(type(Hello))  # <class 'type'>
    print(type(h))   # <class '__main__.Hello'>
    
    # Hello是一个class,类型为type,h是一个实例,它的类型就是class Hello
    
    • type()不仅可以查看一个对象的类型,还可以使用type()函数创建一个class,以下使用type()创建上面的Hello类,无需使用class
    def hl(self, name='world'):
        print('hello %s.' % name)
    Hello = type('Hello', (object,), dict(hello = hl))  # 创建Hello的类
    
    h = Hello()
    h.hello()
    print(type(Hello))
    print(type(h))
    """
    hello world.
    <class 'type'>
    <class '__main__.Hello'>
    """
    
    • 使用type()创建类的三个参数介绍
      • class的名称;
      • 继承的父类集合:tuple的格式(单元素写法),支持多重继承;
      • class的方法名称与函数绑定

    通过type()函数创建类与直接写class是完全一样的,因为Python解释器遇到class定义时,仅是扫描class定义的语法,然后调用type()进行创建class,这也就是动态语言支持运行期间创建类。

    metaclass 元类

    • metaclass的解释
      • 当我们定义了类之后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例
      • 如果我们想创建出类,那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类
      • 因此连接起来就是:先定义metaclass,就可以创建类,然后创建实例。

    所以 metaclass 允许你创建类或修改类,也就是你可以把类看成是 metaclass 创建出来的“实例”。

    • 实际运用很少,后续略…

    个人博客:Loak 正 - 关注人工智能及互联网的个人博客
    文章地址:Python基础(十二)—面向对象拾遗(_slots_、@property、枚举类、元类)

  • 相关阅读:
    BZOJ4779: [Usaco2017 Open]Bovine Genomics
    USACO比赛题泛刷
    BZOJ1977: [BeiJing2010组队]次小生成树 Tree
    LOJ #10132. 「一本通 4.4 例 3」异象石
    $O(n+log(mod))$求乘法逆元的方法
    BZOJ2226: [Spoj 5971] LCMSum
    数据库 | Redis 缓存雪崩解决方案
    中间件 | 微服务架构
    数据库 | SQL 诊断优化套路包,套路用的对,速度升百倍
    数据库 | SQL语法优化方法及实例详解
  • 原文地址:https://www.cnblogs.com/l0zh/p/13739750.html
Copyright © 2020-2023  润新知