博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
『流畅的Python』第9章笔记_对象
阅读量:6187 次
发布时间:2019-06-21

本文共 3639 字,大约阅读时间需要 12 分钟。

一、Python风格 

以一个二元素向量对象为例

import mathfrom array import arrayclass Vector2d:    typecode = 'd'    def __init__(self, x, y):        self.x = float(x)        self.y = float(y)    def __iter__(self):        # 使得Vector2d变成可迭代对象        # __iter__方法的实现使得本类可以被转化为tuple在内的其他可迭代类        return (i for i in (self.x, self.y))    def __repr__(self):        class_name = type(self).__name__  # type(self): 
return '{}({!r},{!r})'.format(class_name, *self) def __str__(self): return str(tuple(self)) def __eq__(self, other): return tuple(self) == tuple(other) def __abs__(self): return math.hypot(self.x, self.y) def __bool__(self): return bool(abs(self)) def __bytes__(self): """将Vector2d对象处理为二进制序列,格式我们自定""" # d:double类型数组 return (bytes([ord(self.typecode)]) + bytes(array(self.typecode, self))) # —————备用析构方法—————— @classmethod # 类方法,cls表示类本身 def frombytes(cls, octets): """对应于上面的方法,这里创建一个新的析构函数,使用特定的二进制序列构造Vector2d类实例""" typecode = chr(octets[0]) memv = memoryview(octets[1:]).cast(typecode) return cls(*memv) # 类名(参数),可见,类方法常用作备用析构 # —格式化输出— def angle(self): # math.atan(scope)输入为tan值 # math.atan2(y, x)输入为对应向量坐标(起点为原点) return math.atan2(self.y, self.x) def __format__(self, fmt_spec=''): """格式化输出,如果格式末尾为p则输出极坐标, 输入其他格式为数字型格式,一个输入格式指定到两个数上,如:.3ep""" if fmt_spec.endswith('p'): fmt_spec = fmt_spec[:-1] coords = (abs(self), self.angle()) out_fmt = '<{}, {}>' else: coords = self out_fmt = '({}, {})' components = (format(c, fmt_spec) for c in coords) return out_fmt.format(*components)

此时这个对象支持大部分python操作,

if __name__ == '__main__':    b = bytes(Vector2d(3, 4))    print(Vector2d.frombytes(b))    print(format(Vector2d(1, 1), '.5fp'))

(3.0, 4.0)

<1.41421, 0.78540>

但是一个重要的方法还是没能实现,__hash__,这关乎到对象是否可以被存入字典进行高速读取的属性,实际上可以hash对象需要三个条件:

  1. 需要__hash__方法
  2. 需要__eq__方法(已经实现)
  3. 需要对象不可变  # 实例的散列值关乎查找等使用方式,绝对不可以变化

也就是我们指定v.x=1(v为class实例)会报错才行,这需要一些其他操作:

class Vector2d:    typecode = 'd'    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 __hash__(self):        return hash(self.x) ^ hash(self.y)  

其他方法不需要修改,

v1 = Vector2d(3, 4)

v2 = Vector2d(3.1, 4.2)

print(hash(v1), hash(v2))

# 7 384307168202284039

 二、类方法和静态方法

# —对比类方法和静态方法—class Demo:    @classmethod    def klassmeth(*args):        return args    @ staticmethod    def statmeth(*args):        return args    def normal(*args):        return args

和实例方法不同,类方法第一个参数永远是类本身,所以常用于备用析构,静态方法没有默认的首位参数,测试如下:

print(Demo.klassmeth("hello"))

print(Demo.statmeth("hello"))
demo = Demo()
print(demo.normal("hello"))

# (<class '__main__.Demo'>, 'hello')

# ('hello',)
# (<__main__.Demo object at 0x000000000289F978>, 'hello')

三、私有属性和受保护属性

两个前导下划线"__",一个或者没有后置下划线的实例属性(self.属性名)为私有变量,会被存入__dict__中,且名称被改写为"_类名__属性名",主要目的是防止类被继承以后,子类实例的继承属性(未在子类中显式的声明)被错误的改写。

注意,__dict__不仅仅存储私有变量,实例属性均存放在__dict__中(默认情况下)。

四、__slots__类属性节约存储空间

class Vector2d:    __slots__ = ('__x', '__y')    typecode = 'd'

 

类属性__slots__为一个存储字符串的可迭代对象,其中的各个字符串是不同的实例属性名,使用tuple是作者推荐的方式,因为可以保证信息不被改动。使用它可以有效节约存储空间,尤其是需要创建大量实例的时候(运行速度往往也更快)。

  1. 继承会自动忽略__slot__属性,所以子类需要显式的定义它
  2. 定义了__slots__后,用户不可以自行添加其他实例属性,但是如果把__dict__存储在__slots__中,就可以添加了,不过就完全没意义了……
  3. 如果想要支持弱引用,需要手动将__weakref__添加进来,虽然自定义class默认存在__weakref__属性,但是想要让实例成为弱引用目标还是需要添加进来才可以。

五、覆盖类属性

实例调用的实例属性时,如果属性不存在,会去读取同名的类属性(例子中的typecode类属性被self.typecode调用)

实例新建实例属性时,如果已经存在同名的类属性,不会改写类属性,而会对实例新建一个实例属性

 

转载地址:http://hboda.baihongyu.com/

你可能感兴趣的文章
[Todo] Nodejs学习及Spider实验(包括php入门学习、React入门学习)
查看>>
笔记本在安装Windows+Linux双系统后,进入Windows时花屏的解决办法
查看>>
【转】百度面试
查看>>
java集合框架
查看>>
智课雅思词汇---十六、前缀hyper和hypo是反义词
查看>>
AsyncTask2
查看>>
区间覆盖(线段树)
查看>>
java读取excel
查看>>
html_之css
查看>>
读书技巧
查看>>
select有条件in要按照in中的数据排序
查看>>
a各种状态
查看>>
Boostrap常用组件英文名
查看>>
python局部赋值规则
查看>>
Notepad++的列编辑功能
查看>>
编码规范
查看>>
基于OpenGL编写一个简易的2D渲染框架-04 绘制图片
查看>>
Markdown基本语法总结
查看>>
PCA and ZAC Whitening
查看>>
android raw与assets区别
查看>>