流畅的Python(九)-符合Python风格的对象

一、核心要义

接续第一章,说明如何实现很多Python类型中常见的特殊方法

1. 支持使用生成对象其他表示形式的内置函数(如repr(),bytes()等)

2. 使用类方法,实现备选构造方法

3.扩展内置的format()函数和str.format()方法使用的格式微语言

4.实现只读属性

5.把对象变成可散列的,以便在集合中作为dict的键使用

6.利用__slots__节省内存

二、代码示例

1、对象表现形式

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/12 10:54
# @Author  : Maple
# @File    : 01-对象表现形式.py
# @Software: PyCharm
import math
from array import array


class Vector:
    typecode = 'd'

    def __init__(self,x,y):
        self.x = x
        self.y =y

    def __iter__(self):
        return (i for i in (self.x,self.y))

    def __repr__(self):
        class_name = type(self).__name__
        # Vector实现iter方法,因此是可迭代对象,可以通过*self获取迭代结果(self.x,self.y)
        return '{}({!r},{!r})'.format(class_name,*self)

    def __str__(self):
        # 从可迭代对象vector可得到元组(self.x,self.y)
        return str(tuple(self))

    def __bytes__(self):
        # 1. ord(self.typecode)将'd'转成对应ASCII码
        # 2. array(self.typecode,self):将vector对象转成float数组
        """
        (1)第一个参数是编码规则
        (2)第二个参数需要是列表、字符串或者可迭代对象(当然具体支持什么类型,需要与typecode相匹配)
           比如,如果第二个参数是str类型,需要typecode是 'u'才支持,因为 'u'  Unicode character  2 (see note)

        array(typecode [, initializer]) -> array

    Return a new array whose items are restricted by typecode, and
    initialized from the optional initializer value, which must be a list,
    string or iterable over elements of the appropriate type.

    Arrays represent basic values and behave very much like lists, except
    the type of objects stored in them is constrained. The type is specified
    at object creation time by using a type code, which is a single character.
    The following type codes are defined:

        Type code   C Type             Minimum size in bytes
        'b'         signed integer     1
        'B'         unsigned integer   1
        'u'         Unicode character  2 (see note)
        'h'         signed integer     2
        'H'         unsigned integer   2
        'i'         signed integer     2
        'I'         unsigned integer   2
        'l'         signed integer     4
        'L'         unsigned integer   4
        'q'         signed integer     8 (see note)
        'Q'         unsigned integer   8 (see note)
        'f'         floating point     4
        'd'         floating point     8


        """
        #
        return bytes([ord(self.typecode)]) + bytes(array(self.typecode,self))

    @classmethod
    def frombytes(cls,octets):
        """类方法常见用途是定义备选构造方法"""
        # 获取编码规则
        typecode = chr(octets[0])
        # 获取需要构造方法所需要的参数
        memv = memoryview(octets[1:]).cast(typecode)
        # 返回对象
        return cls(*memv)


    def __eq__(self, other):
        return tuple(self) == tuple(other)

    def __abs__(self):
        return math.hypot(self.x,self.y)

    def __bool__(self):
        # 1. abs(self)会调用self.__abs__
        # 2. 如果vector对象的abs值大于0,则返回True,否则False
        return bool(abs(self))

if __name__ == '__main__':

    # 1. 打印实例属性值
    print('************1. 打印实例属性值*****************')
    v1 = Vector(3,4)
    print(v1.x,v1.y)

    # 2. Vector实例对象是可迭代的,因此会拆包成变量元组
    print('************2. Vector实例对象拆包成变量元组*****************')
    x,y = v1
    print(x,y)

    # 3.如果Vector没有实现__str__方法,会按照__repr__定义的格式打印v1
    print('************3. repr方式打印对象*****************')
    print(v1) # Vector(3,4)

    # 4.如果同时定义了__str__和__repr__,则会按照__str__的格式打印v1
    print('************4. str方式打印对象*****************')
    print(v1) # (3,4)

    # 5.eval方式构造对象
    print('************5. eval方式构造对象*****************')
    clone_v1 = eval(repr(v1)) # 相当于执行Vector(3,4),克隆了一份v1对象
    # clone_v1和v1是两个不同的对象
    print(clone_v1 is v1) #False
    # clone_v1和v1的值相等(调用__eq__方法进行比较)
    print(clone_v1 == v1) #True

    # 6.获取对象的二级制转换结果
    print('************6.获取对象的二级制转换结果*****************')
    octets = bytes(v1)
    print(octets) # b'd\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@'

    # 基于octets 利用frombytes类方法构建对象
    print('********基于octets 利用frombytes类方法构建对象*****************')
    v2 = Vector.frombytes(octets)
    print(v1 ==v2 ) # True

    # 7. 获取对象abs
    print('********7. 获取对象abs*****************')
    print(abs(v1))# 5.0

    # 8.bool测试
    print('********8.bool测试*****************')
    print(bool(v1)) #True

2、格式化显示

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/12 21:32
# @Author  : Maple
# @File    : 02-格式化显示.py
# @Software: PyCharm
import math
from array import array


class Vector:
    typecode = 'd'


    def __init__(self,x,y):
        self.x = x
        self.y =y

    def __iter__(self):
        return (i for i in (self.x,self.y))

    def __repr__(self):
        class_name = type(self).__name__
        # Vector实现iter方法,因此是可迭代对象,可以通过*self获取迭代结果(self.x,self.y)
        return '{}({!r},{!r})'.format(class_name,*self)

    def __str__(self):
        # 从可迭代对象vector可得到元组(self.x,self.y)
        return str(tuple(self))

    def __bytes__(self):

        return bytes([ord(self.typecode)]) + bytes(array(self.typecode,self))

    @classmethod
    def frombytes(cls,octets):
        """类方法常见用途是定义备选构造方法"""
        # 获取编码规则
        typecode = chr(octets[0])
        # 获取需要构造方法所需要的参数
        memv = memoryview(octets[1:]).cast(typecode)
        # 返回对象
        return cls(*memv)


    def __eq__(self, other):
        return tuple(self) == tuple(other)

    def __abs__(self):
        return math.hypot(self.x,self.y)

    def __bool__(self):
        # 1. abs(self)会调用self.__abs__
        # 2. 如果vector对象的abs值大于0,则返回True,否则False
        return bool(abs(self))


    # 添加自定的format方法
    def __format__(self, format_spec):
        components = (format(c,format_spec) for c in self)
        return '({},{})'.format(*components)




class Vector2:
    """极坐标格式输出
    1. 如果格式说明符以'p'结尾,那么在极坐标中显示向量 ,其中r是模,θ是弧度,'p'之前的数字部分,按照往常的格式定义输出
    """
    typecode = 'd'


    def __init__(self,x,y):
        self.x = x
        self.y =y

    def __iter__(self):
        return (i for i in (self.x,self.y))

    def __repr__(self):
        class_name = type(self).__name__
        # Vector实现iter方法,因此是可迭代对象,可以通过*self获取迭代结果(self.x,self.y)
        return '{}({!r},{!r})'.format(class_name,*self)

    def __str__(self):
        # 从可迭代对象vector可得到元组(self.x,self.y)
        return str(tuple(self))

    def __bytes__(self):

        return bytes([ord(self.typecode)]) + bytes(array(self.typecode,self))

    @classmethod
    def frombytes(cls,octets):
        """类方法常见用途是定义备选构造方法"""
        # 获取编码规则
        typecode = chr(octets[0])
        # 获取需要构造方法所需要的参数
        memv = memoryview(octets[1:]).cast(typecode)
        # 返回对象
        return cls(*memv)


    def __eq__(self, other):
        return tuple(self) == tuple(other)

    def __abs__(self):
        return math.hypot(self.x,self.y)

    def __bool__(self):
        # 1. abs(self)会调用self.__abs__
        # 2. 如果vector对象的abs值大于0,则返回True,否则False
        return bool(abs(self))

    # 获取向量的角度
    def angel(self):
        return math.atan2(self.y,self.x)


    # 添加自定的format方法
    def __format__(self, format_spec=''):
        if format_spec.endswith('p'):
            new_format = format_spec.replace('p','')
            r = format(abs(self),new_format)
            Θ = format(self.angel(),new_format)
            return '({},{})'.format(r,Θ)
        else:
            components = (format(c, format_spec) for c in self)
            return '({},{})'.format(*components)




if __name__ == '__main__':

    # 1.内置的format方法
    brl = 1/2.34
    print(format(brl,'0.4f')) # 0.4274

    # 2.内置str.format方法
    # 其中rate是参数名,brl是参数值-会以自定义的格式'0.2f'替换rate:后面的部分
    print('1 BRL = {rate:0.2f}'.format(rate = brl)) #1 BRL = 0.43

    # 3.vector类的格式输出
    # 如果类没有自定义format方法,format(v)会返回str(v)的值
    # 如果定义了format方法,则按照format定义的格式输出
    v = Vector(3,4)
    print(format(v,'0.2f')) # (3.00,4.00)
    print(format(v,'.3e')) #(3.000e+00,4.000e+00)

    # 4.vector2类的格式输出
    v2 = Vector2(1,1)
    print(format(v2,'p')) # (1.4142135623730951,0.7853981633974483)
    print(format(v2,'.3ep')) # (1.414e+00,7.854e-01)
    print(format(v2,'0.5fp')) # (1.41421,0.78540)

3、可散列的Vector

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/13 8:59
# @Author  : Maple
# @File    : 03-可散列的Vector.py
# @Software: PyCharm

"""要使Vector实例是可散列的,必须实现__hash__方法,
而要实现__hash__方法,必须保证类的属性是不可变的
"""

class Vector:
    def __init__(self,x,y):
        # 双下划线开头的是私有属性
        self.__x = float(x)
        self.__y = float(y)

    @property
    def x(self):
        return self.__x

    @property
    def y(self):
        return self.__y


    def __iter__(self):
        return (c for c in (self.x,self.y))

    def __hash__(self):
        return hash(self.x) ^ hash(self.y)

if __name__ == '__main__':
    v = Vector(1,2)
    # 1. 访问v实例的x和y属性
    # 本质上会调用def x 和def y方法
    print(v.x,v.y) # 1.0 2.0

    # 2. 修改v实例的x和y属性的值
    ## 2-1 会直接报错,因为类中并没有x属性
    #v.x = 1.5 # AttributeError: can't set attribute

    ## 2-2 相当于给实例新增了一个属性__x,然后赋值1.5,但该属性其实并不是Vector类中定义的属性
    v.__x = 1.5
    print(v.__x) # 1.5

    ## 2-3.私有属性的真正名字是_Vector__x(虽然可以修改,但既然定义为私有属性,当然是不建议从类的外部直接修改该属性的值)
    v._Vector__x = 2.5
    print(v.x) # 2.5

    # 3. Vector对象的hash值
    print(hash(v)) # 1152921504606846976
    ## Vector类实现hash方法后,其实例对象就能够作为集合中的元素了
    v2 = Vector(1.5,2.5)
    s = set([v,v2])
    print(s) # {<__main__.Vector object at 0x000001D50E19BD60>, <__main__.Vector object at 0x000001D50E19BF10>}

4、slot类属性

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/13 19:14
# @Author  : Maple
# @File    : 04-slot类属性.py
# @Software: PyCharm

"""
-背景说明:
1. Python在各个实例的中名为__dict__的字典中存储实例的属性
2. 由于字典的存储方式是散列的,如果属性过多,字典会消耗大量内存
使用场景:
-为了节约内存属性,方法是让解释器在元组中存储实例属性,而非字典
"""

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

class Student:
    # __slots__的目的:告知类的所有实例属性都在这里了,无需在Student实例的__dict__字典中存放属性
    __slots__ = ('name','age')

    def __init__(self,name,age):
        self.name = name
        self.age = age

class Computer:
    # 如果把__dict__ 放进slots,实例会在元组中保存各个实例的属性,此外还支持动态创建属性,这些属性还存储在常规的__dict__中
    __slots__ = ('brand','price','__dict__')

    def __init__(self,brand,price):
        self.brand = brand
        self.price = price


if __name__ == '__main__':
    p = Person('Maple',18)
    # 1.dict字典会存储实例属性
    print(p.__dict__) # {'name': 'Maple', 'age': 18}

    # 2.slot属性定义的类
    s = Student('Maple',18)
    # 测试类的实例的__dict__属性是否仍然存在
    # print(s.__dict__) # Student' object has no attribute '__dict__'

    # 3.slot中包含dict测试
    c = Computer('HTC',1000)
    # 3-1. __dict__字典最初为空
    print(c.__dict__) # {}

    # 3-2.动态添加属性
    c.color = 'red'
    print(c.__dict__)# {'color': 'red'}

5、覆盖类属性

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/2/13 20:57
# @Author  : Maple
# @File    : 05-覆盖类属性.py
# @Software: PyCharm
from array import array


class Vector:
    typecode = 'd'

    def __init__(self,x,y):
        self.x = x
        self.y =y

    def __iter__(self):
        return (i for i in (self.x,self.y))

    def __repr__(self):
        class_name = type(self).__name__
        # Vector实现iter方法,因此是可迭代对象,可以通过*self获取迭代结果(self.x,self.y)
        return '{}({!r},{!r})'.format(class_name,*self)

    def __str__(self):
        # 从可迭代对象vector可得到元组(self.x,self.y)
        return str(tuple(self))

    def __bytes__(self):

        return bytes([ord(self.typecode)]) + bytes(array(self.typecode,self))

    @classmethod
    def frombytes(cls,octets):
        """类方法常见用途是定义备选构造方法"""
        # 获取编码规则
        typecode = chr(octets[0])
        # 获取需要构造方法所需要的参数
        memv = memoryview(octets[1:]).cast(typecode)
        # 返回对象
        return cls(*memv)


    def __eq__(self, other):
        return tuple(self) == tuple(other)

    def __abs__(self):
        return math.hypot(self.x,self.y)

    def __bool__(self):
        # 1. abs(self)会调用self.__abs__
        # 2. 如果vector对象的abs值大于0,则返回True,否则False
        return bool(abs(self))


    # 添加自定的format方法
    def __format__(self, format_spec):
        components = (format(c,format_spec) for c in self)
        return '({},{})'.format(*components)

class SubVector(Vector):
    # 覆盖父类的typecode属性
    typecode = 'f'


if __name__ == '__main__':
    # 1. Vector类中类属性typecode的默认值是'd',表示8字节双精度浮点数
    v1 = Vector(1,2)
    print(bytes(v1)) # b'd\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@'

    # 2.修改实例v1的typecode属性为'f',表示4字节双精度浮点数
    v1.typecode = 'f'
    print(bytes(v1)) # b'f\x00\x00\x80?\x00\x00\x00@'

    # 3.类的typecode属性并不受影响,仍然为'd'
    print(Vector.typecode) # d

    # 4.可按以下方式修改类属性
    Vector.typecode = 'f'
    v2 = Vector(1,2)
    print(bytes(v2)) # b'f\x00\x00\x80?\x00\x00\x00@'

    # 5.另外一种修改类属性的方法是:子类覆盖父类typecode属性
    s1 = SubVector(1,2)
    print(bytes(s1)) # b'f\x00\x00\x80?\x00\x00\x00@'

你可能感兴趣的:(Python,python,开发语言,数据库)