• cookbook_类与对象


    1修改实例的字符串表示

    可以通过定义__str__()和__repr__()方法来实现

    class Pair:
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __str__(self):
            return "(%s,%s)"%(self.x,self.y)
    
        def __repr__(self):
            return "Pair(%s,%s)"%(self.x,self.y)
    
    p = Pair(2,5)
    print(p)
    print("p is {0!r}".format(p))

    对于__repr__(),标准的方法是让他产生的字符串文本能够满足eval(repr(x)) == x

    __str__()则产生一段有意义的文本

     

    2自定义字符串的输出格式

     

    我们想让对象通过format()函数和字符串方法来支持自定义的输出格式

    要自定义字符串的输出格式,可以在类中定义__format__()方法 

    _formats = {
        "ymd":"{d.year}-{d.month}-{d.day}",
        "mdy":"{d.month}/{d.day}/{d.year}",
        "dmy":"{d.day}/{d.month}/{d.year}"
    }
    
    class Date:
        def __init__(self,year,month,day):
            self.year = year
            self.month = month
            self.day = day
    
        def __format__(self,code):
            if code == "":
                code = "ymd"
            fmt = _formats[code]
            return fmt.format(d = self)
    
    d = Date(2018,9,26)
    print(format(d))
    print(format(d,"dmy"))
    print(format(d,"mdy"))

    3让对象支持上下文管理协议

    我们想让对象支持上下文管理协议,即可以通过with语句触发。

    想让对象支持上下文管理协议,对象需实现__enter__()和__exit__()方法,比如实现网络连接的类。

    from socket import socket,AF_INET,SOCK_STREAM
    
    class LazyConnection:
        
        def __init__(self,address,family = AF_INET, type = SOCK_STREAM):
            self.address = address
            self.family = family
            self.type = type
            self.sock = None
    
        def __enter__(self):
            if self.sock is not None:
                raise RuntimeError("Already connected")
            self.sock = socket(self.family,self.type)
            self.sock.connect(self.address)
            return self.sock
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.sock.close()
            self.sock = None
    
    conn = LazyConnection("www.baidu.com")
    
    with conn as s:
        s.send(b'hahhahah')

    4当创建大量实例时如何节省内存

    当我们的程序需要创建大量的实例(百万级),这样会占用大量的内存。

    #对于那些主要用作简单数据结构的类,通常可以在类定义中增加__slot__属性,以此来大量减少对内存的使用。

    class Date:
        __slots__ = ["year","month","day"]
    
        def __init__(self,year,month,day):
            self.year = year
            self.month = month
            self.day = day

    当定义了__slots__属性时,python会采用一种更加紧凑的内部表示,会将实例的属性添加到一个小型数组里,不再为每个实例创建__dict__。

    副作用是我们不能为实例添加新的属性。是一种优化手段

    5将名称封装到类中

    在python中,以单下划线_开头的属性被认为是一种私有属性

    class A:
        def __init__(self):
            self._name = "jiaojianglong"
            self.age = 24
    
        def _internal_method(self):
            print("i am a internal method")
    
    
    a = A()
    print(a._name) #jiaojianglong

    python并不会阻止访问属性,但编译器不会做提示。如果强行访问会被认为是粗鲁的。

    在类的定义中也见到过双下划线__开头的名称,以双下划线开头的名称会导致出现名称重组的行为

    class B:
        def __init__(self):
            self.__name = "jiaojianglong"
    
    b = B()
    # print(b.__name)#AttributeError: 'B' object has no attribute '__name'
    print(b._B__name)#jiaojianglong

    这样的行为是为了继承,以双下划线开头的属性不会被子类通过继承而覆盖。

    class C(B):
        def __init__(self):
            super().__init__()
    
    c = C()
    print(c._B__name)#jiaojianglong

     

    大部分情况下我们使用单下划线,涉及到子类继承覆盖的问题时使用双下划线

    当我们想定义一个变量,但是名称可能会与保留字段冲突,基于此,我们在名称后加一个单下划线以示区别。lambda_

    6创建可管理的属性

    #在对实例的获取和设定上,我们希望增加一些额外的处理过程。

    class Person:
        def __init__(self,first_name):
            self.first_name = first_name
    
        @property
        def first_name(self):
            return self._first_name
    
        @first_name.setter
        def first_name(self,value):
            if not isinstance(value,str):
                raise TypeError("Excepted a string")
            self._first_name = value
    
    
    
    p = Person("jiao")
    print(p.first_name)

    在创建实例时,__inti__()中我们将name赋值到self.first_name,实际会调用setter方法,所以name实际还是储存在self._first_name中

    7调用父类中的方法

    #我们想调用一个父类中的方法,这个方法在子类中已经被覆盖了。

    class A:
        def spam(self):
            print("A.spam")
    
    class B(A):
        def spam(self):
            print("B.spam")
            super().spam()
    
    b = B().spam()#B.spam,A.spam
    
    print(B.__mro__)#(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

    争对每一个类,python都会计算出一个称为方法解析顺序(MRO)的列表,MOR列表只是简单的对所有的基类进行线性排列。

     

    8在子类中扩展属性

    我们想在子类中扩展某个属性的功能,而这个属性是在父类中定义的

    9创建一种新形式的类属性或实例属性

    如果想定义一种新形式的实例属性,可以以描述符的形式定义其功能。

    class Integer():
    
        def __init__(self,name):
            self.name = name
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            else:
                return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            if not isinstance(value,int):
                raise TypeError("Expected an int")
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            del instance.__dict__[self.name]
    
    
    class Point:
        x = Integer("x")
        y = Integer("y")
    
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
    p = Point(2,3)
    print(p.x)#2
    p.y = 5
    print(p.y)#5
    # p.x = "a"#TypeError: Expected an int
    print(Point.x)#<__main__.Integer object at 0x00000141E2ABB5F8>

    __get__()方法看起来有些复杂的原因是实例变量和类变量的区别,如果是类变量则简单的返回描述符本身,如果是实例变量返回定义的值

    关于描述符,常容易困惑的地方就是他们只能在类的层次上定义,不能根据实例来产生,下面的代码是无法工作的

    class Point:
    
        def __init__(self,x,y):
            self.x = Integer("x")
            self.y = Integer("y")
            self.x = x
            self.y = y
    
    p = Point(2,"c")
    print(p.x)#2
    print(p.y)#c
    
    class Typed:
        def __init__(self,name,expected_type):
            self.name = name
            self.expected_type = expected_type
    
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            else:
                return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            if not isinstance(value,self.expected_type):
                raise TypeError("Expected %s"%self.expected_type)
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            del instance.__dict__[self.name]
    
    def typeassert(**kwargs):
        def decorate(cls):
            for name,expected_type in kwargs.items():
                setattr(cls,name,Typed(name,expected_type))
            return cls
        return decorate
    
    @typeassert(name=str,shares = int,price=float)
    class Stock:
        def __init__(self,name,shares,price):
            self.name = name
            self.shares = shares
            self.price = price

    对于少量的定制还是使用property简单些,如果是大量的定制则使用描述符要简单些

  • 相关阅读:
    Java后台校验手机号和邮箱
    centos7安装redis
    centos7安装mysql5.7
    VUE环境运行搭建
    Oracle11gR2安装
    VisualSvn-Server搭建
    SpringCloud-Hoxton.SR1-config整合
    项目简介
    linux常用命令
    JAVA NIO
  • 原文地址:https://www.cnblogs.com/jiaojianglong/p/11260151.html
Copyright © 2020-2023  润新知