zoukankan      html  css  js  c++  java
  • 【Python】__slots__ 、@property、多重继承、定制类、枚举类、元类

    __slots__

    @property

    多重继承

    定制类

    枚举类

    元类

    【使用__slots__】

    1、动态语言的一个特点就是允许给实例绑定任意的方法和变量,而静态语言(例如Java)必须事先将属性方法写进类里。

    给实例绑定变量:

    >>> class Student:
    ...     pass
    ...
    >>> s = Student()
    >>> s.name = 'Lily'
    >>> s.name
    'Lily'

    给实例绑定方法需要借助types模块的MethodType方法:

    >>> def setAge(self, age):
    ...     self.age = age
    ...
    >>> from types import MethodType
    >>> s.setAge = MethodType(setAge, s)
    >>> s.setAge(5)
    >>> s.age
    5

    还可以给class绑定方法,使得方法对所有实例都有效:

    >>> Student.setAge = setAge

    2、万一需要限制属性怎么办?即只允许类的实例添加有限个属性。

    这个时候可以使用一个特殊的类变量__slots__:

    >>> class Employee(object):
    ...     __slots__ = ('name', 'salary')
    ...
    >>> e = Employee()
    >>> e.name = 'Wang fang'
    >>> e.salary = 18000
    >>> e.age = 18 # age无法被添加为实例的属性!
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Employee' object has no attribute 'age'

    但是需要注意的是,__slots__对继承的子类是不起作用的!

    【使用@property】

    1、简而言之,@property就是一个类的方法的“装饰器”,添加在类的方法名的上一行,作用是把一个方法变成属性调用(即允许s.field这种访问方式),又能进行适当的参数检查。一个简单的例子:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    class Movie(object):
    
        # 把一个getter方法变成属性
        @property 
        def title(self):
            return self._title # 不能和装饰器@title.setter重名,所以使用_title
    
        @title.setter
        def title(self, title):
            if not isinstance(title, str):
                raise ValueError('title must be a str!')
            self._title = title
    
    # 类测试
    if __name__ == '__main__':
        m = Movie()
        # m.title = 123
        m.title = 'Tokyo Ghoul'
        print(m.title)

    如果输入的title不是str类型,则会输出:

    Traceback (most recent call last):
      File "D:labs	est.py", line 20, in <module>
      File "D:labs	est.py", line 14, in title
    ValueError: title must be a str!

    2、如果要将上面的title属性改为只读,只需要删去setter方法就可以了。

    3、练习。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    class Screen(object):
    
        @property
        def width(self):
            return self._width
        
        @width.setter
        def width(self, width):
            if width <= 0:
                raise ValueError('width must larger than 0')
            self._width = width
        
        @property
        def height(self):
            return self._height
        
        @height.setter
        def height(self, height):
            if height <= 0:
                raise ValueError('height must larger than 0')
            self._height = height
        
        @property
        def area(self):
            return self._width * self._height

    测试结果:

    >>> from test import Screen
    >>> s = Screen()
    >>> s.width = 0
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "D:labs	est.py", line 13, in width
        raise ValueError('width must larger than 0')
    ValueError: width must larger than 0
    >>> s.width = 80
    >>> s.height = 70
    >>> s.area = 10
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute
    >>> s.area
    5600

    【多重继承】

    1、多重继承:类似于Java可以实现多个接口。(仅仅是类似,不等于!)

    2、引用网友:第一个继承的是生父,后边的都是继父,继父的名字后面要加MixIn表明身份,如果方法有冲突,优先继承生父的。

    class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
        pass

    注:第一个是主线继承,后两个是增加的功能。

      

    【定制类】

    1、__str__()__repr__()类似于Java中的toString()。前者用于s.print(object)、后者用于直接输入s时的输出。

    def __str__(self):
    ...         return 'Student object (name: %s)' % self.name

    偷懒的写法:写完__str__()后,__repr__ = __str__

    2、__iter__()__next__()。简单的例子:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    class f():
        def __init__(self):
            self.a = 0
    
        def __iter__(self):
            return self # 返回一个迭代对象给for
    
        def __next__(self): # 实现一个next方法给for调用
            self.a += 1
            return self.a 

    4、__getitem__:让自定义对象表现得像list、str等类型一样。(可以进行索引、切片等操作)

    class f():
        def __getitem__(self, n):
            return n
    >>> from test import f
    >>> f()[1]
    1
    >>> f()[3]
    3

     如果要支持切片:

    class f():
        def __getitem__(self, n):
            if isinstance(n, int):
                return n
            if isinstance(n, slice):
                start = n.start
                stop = n.stop
                if start == None: #例如[:3]默认为[0:3]
                    start = 0
                L = []
                for x in range(start, stop):
                    L.append(x)
                return L
    >>> f()[1:3]
    [1, 2]

    5、__getattr__:attr是attribute的简写。当实例本身不具备某个属性,却又强行被调用的时候会调用该函数。

    class f():
        def __init__(self):
            self.a = 1111
            self.b = 33333
        
        def __getattr__(self, attr):
            return "i had not attribute " + attr + "!"
    >>> f().a
    1111
    >>> f().b
    33333
    >>> f().c
    'i had not attribute c!'

     当然也可以返回函数。

    利用完全动态的__getattr__,可以写出一个链式调用。。。

    6、__call__

    1)判断一个变量是对象还是函数:callable()。

    2)把对象变成函数的方法:重写__call__方法。

    such:

    class f:
        def __call__(self, a, b):
            return a + b
    >>> sum = f()
    >>> sum(1, 2)
    3

    更多python的定制方法参考python官方文档。

    【使用枚举类】

    在Python中枚举类型也可以被视为一个类。

    1、有两种方式可以创建这种类。

    1)赋值法。

    >>> from enum import Enum
    >>> Colors = Enum('Colors', ('RED', 'YELLOW', 'BLUE'))

    自然的操作,尝试打印一个Enum常量:

    >>> Colors.RED
    <Colors.RED: 1> 

    2)继承Enum。

    from enum import Enum, unique
    
    @unique
    class Colors(Enum): RED = 10086 YELLOW = 111 BLUE = 2222

    尝试打印:

    >>> from test import Colors
    >>> Colors.RED
    <Colors.RED: 10086>

    10086即是RED的value。ps:@unique装饰器可以帮助检查有没有重复值。

    2、枚举类用于逻辑关系运算 & 打印枚举类型变量

    >>> myColor = Colors.BLUE
    >>> myColor == Colors.RED
    False
    >>> print(myColor)
    Colors.BLUE

    【使用元类】

     1、Python中的type()。

    type()是用来干嘛的呢?根据字面上的意思很容易想到,可以通过type来输出Python中各种构件的类型,例如:

    >>> type(1)
    <class 'int'>
    >>> type('hi python')
    <class 'str'>
    >>> type(lambda x: x)
    <class 'function'>

    尝试传入类名、对象会发现:

    >>> class MyClass(object):
    ...     pass
    ...
    >>> type(MyClass)
    <class 'type'>
    >>> type(MyClass())
    <class '__main__.MyClass'>

    MyClass()对象的类型是MyClass,而MyClass类的类型是type!

    这就说明,MyClass本身也可以被看作是由type创建的对象,而MyClass这个对象又具备创建对象的能力。

    因此,type()除了可以用于查看各种Python构件的类型外,还可以用来创建类。

    实际上在Python的解释器内部创建类的工作都是自动调用type()完成的。那么如何手动调用type()呢?

    只需要将传统的class创建方式映射到type()上来就可以了。

    一个传统的dog:

    class Dog(object):
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def run(self):
            print(self.name + ' run!
    ')
    >>> from test import Dog
    >>> i = Dog('Huahua', 2)
    >>> i.run()
    Huahua run!

    一个type()创建的dog类:

    # 先把组件写好
    
    # 构造器
    def f1(self, name, age):
        self.name = name
        self.age = age
    
    # run方法
    def f2(self):
        print(self.name + ' run!
    ')
    
    # 组装成type()
    Dog = type('Dog', (object,), dict(__init__=f1, run=f2))

    测试:

    >>> def f1(self, name, age):
    ...     self.name = name
    ...     self.age = age
    ...
    >>> def f2(self):
    ...     print(self.name + ' run!
    ')
    ...
    >>> Dog = type('Dog', (object,), dict(__init__=f1, run=f2))
    >>> i = Dog('Lele', 3)
    >>> i.run()
    Lele run!

     为什么使用type()而不是class来创建类呢?因为type()更具动态性。

    2、元类。参考博客:深刻理解Python中的元类

    为什么使用metaclass?和使用type一样,为了创建基于上下文的类。

    什么是metaclass?type就是Python内置的一个metaclass!

    一个使用metaclass会让事情变得简单的例子:数据库的GUI。(用type做不了,非metaclass不可)

    定义metaclass

    用metaclass创建类

    这里是FO是

  • 相关阅读:
    cocos日记
    Android 开发经验
    vbs自学备份
    C# 如何在winform中嵌入Excel,内嵌Excel,word
    win7 64位在线编辑dsoframer控件的安装和使用配置
    C# 正则表达式 最全的验证类
    在 Range 对象中,Min (14)必须小于或等于 max (-1)。
    winform Form窗体和UserControl用户空间嵌入Panel容器并填充
    C# Winform防止闪频和再次运行
    Base64编码解码
  • 原文地址:https://www.cnblogs.com/xkxf/p/6616485.html
Copyright © 2011-2022 走看看