Python语言开发学习之Python如何设计面向对象的类1
小职 2021-07-16 来源 :dongfanger 阅读 339 评论 0

摘要:本文主要介绍Python语言开发学习之Python如何设计面向对象的类1,通过具体的内容向大家展现,希望对大家Python程序开发的学习有所帮助。

本文主要介绍Python语言开发学习之Python如何设计面向对象的类1,通过具体的内容向大家展现,希望对大家Python程序开发的学习有所帮助。

Python语言开发学习之Python如何设计面向对象的类1

Python是一门高级语言,支持面向对象设计,如何设计一个符合Python风格的面向对象的类,是一个比较复杂的问题,本文提供一个参考,表达一种思路,探究一层原理。

 

目标

期望实现的类具有以下基本行为:

 

__repr__ 为repr()提供支持,返回便于开发者理解的对象字符串表示形式。

__str__ 为str()提供支持,返回便于用户理解的对象字符串表示形式。

__bytes__ 为bytes()提供支持,返回对象的二进制表示形式。

__format__ 为format()和str.format()提供支持,使用特殊的格式代码显示对象的字符串表示形式。

Vector2d是一个向量类,期望它能支持以下操作:

 

>>> v1 = Vector2d(3, 4)

>>> print(v1.x, v1.y)  # 通过属性直接访问

3.0 4.0

>>> x, y = v1  # 支持拆包

>>> x, y

(3.0, 4.0)

>>> v1  # 支持repr

Vector2d(3.0, 4.0)

>>> v1_clone = eval(repr(v1))  # 验证repr描述准确

>>> v1 == v1_clone  # 支持==运算符

True

>>> print(v1)  # 支持str

(3.0, 4.0)

>>> octets = bytes(v1)  # 支持bytes

>>> octets

b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'

>>> abs(v1)  # 实现__abs__

5.0

>>> bool(v1), bool(Vector2d(0, 0))  # 实现__bool__

(True, False)

基本实现

代码与解析如下:

 

from array import array

import math

 

 

class Vector2d:

    # Vector2d实例和二进制之间转换时使用

    typecode = 'd'   

 

    def __init__(self, x, y):

        # 转换为浮点数

        self.x = float(x)     

        self.y = float(y)

 

    def __iter__(self):

        # 生成器表达式,把Vector2d实例变成可迭代对象,这样才能拆包

        return (i for i in (self.x, self.y))   

 

    def __repr__(self):

        class_name = type(self).__name__

        # {!r}是个万能的格式符

        # *self是拆包,*表示所有元素

        return '{}({!r}, {!r})'.format(class_name, *self)

 

    def __str__(self):

        # Vector2d实例是可迭代对象,可以得到一个元组,并str

        return str(tuple(self))

 

    def __bytes__(self):

        # 转换为二进制

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

                bytes(array(self.typecode, self)))   

 

    def __eq__(self, other):

        # 比较相等

        return tuple(self) == tuple(other)   

 

    def __abs__(self):

        # 向量的模是直角三角形的斜边长

        return math.hypot(self.x, self.y)  

 

    def __bool__(self):

        # 0.0是False,非零值是True

        return bool(abs(self))   

     

    @classmethod

    def frombytes(cls, octets):  # classmethod不传self传cls

        typecode = chr(octets[0])

        memv = memoryview(octets[1:]).cast(typecode)

        return cls(*memv)  # 拆包后得到构造方法所需的一对参数

代码最后用到了@classmethod装饰器,它容易跟@staticmethod混淆。

 

@classmethod的用法是:定义操作类,而不是操作实例的方法。常用来定义备选构造方法。

 

@staticmethod其实就是个普通函数,只不过刚好放在了类的定义体里。实际定义在类中或模块中都可以。

 

格式化显示

代码与解析如下:

 

def angle(self):

    return math.atan2(self.y, self.x)

 

 

def __format__(self, fmt_spec=''):

    if fmt_spec.endswith('p'):  # 以'p'结尾,使用极坐标

        fmt_spec = fmt_spec[:-1]

        coords = (abs(self), self.angle())  # 计算极坐标(magnitude, angle)

        outer_fmt = '<{}, {}>'  # 尖括号

    else:

        coords = self  # 不以'p'结尾,构建直角坐标(x, y)

        outer_fmt = '({}, {})'  # 圆括号

    components = (format(c, fmt_spec) for c in coords)  # 使用内置format函数格式化字符串

    return outer_fmt.format(*components)  # 拆包后代入外层格式

它实现了以下效果:

 

直角坐标:

 

>>> format(v1)

'(3.0, 4.0)'

>>> format(v1, '.2f')

'(3.00, 4.00)'

>>> format(v1, '.3e')

'(3.000e+00, 4.000e+00)'

极坐标:

 

>>> format(Vector2d(1, 1), 'p')  # doctest:+ELLIPSIS

'<1.414213..., 0.785398...>'

>>> format(Vector2d(1, 1), '.3ep')

'<1.414e+00, 7.854e-01>'

>>> format(Vector2d(1, 1), '0.5fp')

'<1.41421, 0.78540>'

可散列的

实现__hash__特殊方法能让Vector2d变成可散列的,不过在这之前需要先让属性不可变,代码如下:

 

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

这样x和y就只读不可写了。

 

属性名字的双下划线前缀叫做名称改写(name mangling),相当于_Vector2d__x和_Vector2d__y,能避免被子类覆盖。

 

然后使用位运算符异或混合x和y的散列值:

 

def __hash__(self):

    return hash(self.x) ^ hash(self.y)

节省内存

Python默认会把实例属性存储在__dict__字典里,字典的底层是散列表,数据量大了以后会消耗大量内存(以空间换时间)。通过__slots__类属性,能把实例属性存储到元组里,大大节省内存空间。

 

示例:

 

class Vector2d:

    __slots__ = ('__x', '__y')

 

    typecode = 'd'

有几点需要注意:

 

必须把所有属性都定义到__slots__元组中。

 

子类也必须定义__slots__。

 

实例如果要支持弱引用,需要把__weakref也加入__slots__。

 

覆盖类属性

实例覆盖

 

Python有个很独特的特性:类属性可用于为实例属性提供默认值。实例代码中的typecode就能直接被self.typecode拿到。但是,如果为不存在的实例属性赋值,会新建实例属性,类属性不会受到影响,self.typecode拿到的是实例属性的typecode。

 

示例:

 

>>> v1 = Vector2d(1, 2)

>>> v1.typecode = 'f'

>>> v1.typecode

'f'

>>> Vector2d.typecode

'd'

子类覆盖

 

类属性是公开的,所以可以直接通过Vector2d.typecode = 'f'进行修改。但是更符合Python风格的做法是定义子类:

 

class ShortVector2d(Vector2d):

    typecode = 'f'

Django基于类的视图大量使用了这个技术。

 

小结

本文先介绍了如何实现特殊方法来设计一个Python风格的类,然后分别实现了格式化显示与可散列对象,使用__slots__能为类节省内存,最后讨论了类属性覆盖技术,子类覆盖是Django基于类的视图大量用到的技术。


我是小职,记得找我

✅ 解锁高薪工作

✅ 免费获取基础课程·答疑解惑·职业测评

Python语言开发学习之Python如何设计面向对象的类1

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程