类与对象--cookbook读书笔记

对象字符串显示

重新定义它的__str__() 和__repr__() 方法,使用str() 或print() 函数会输出__str__字符串(更适合人阅读的字符串),repr() 函数返回__repr__字符串(更适合机器阅读),__repr__() 生成的文本字符串标准做法是需要让eval(repr(x)) == x 为真。如果实在不能这样子做,应该创建一个有用的文本表示,并使用< 和> 括起来。如果__str__() 没有被定义,那么就会使用__repr__() 来代替输出。

自定义字符串的格式化

为了自定义字符串的格式化,我们需要在类上面定义__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(2012, 12, 21)
>>> format(d)
'2012-12-21'
>>> format(d, 'mdy')
'12/21/2012'
>>> 'The date is {:ymd}'.format(d)
'The date is 2012-12-21'
>>> 'The date is {:mdy}'.format(d)
'The date is 12/21/2012'
>>>

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

为了让一个对象兼容with 语句,你需要实现__enter__() 和__exit__() 方法。编写上下文管理器的主要原理是你的代码会放到with 语句块中执行。当出现with语句的时候,对象的__enter__() 方法被触发,它返回的值(如果有的话) 会被赋值给as 声明的变量。然后,with 语句块里面的代码开始执行。最后, __exit__() 方法被触发进行清理工作。
不管with 代码块中发生什么,上面的控制流都会执行完,就算代码块中发生了异常也是一样的。事实上,__exit__() 方法的第三个参数包含了异常类型、异常值和追溯信息(如果有的话)。__exit__() 方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个 None 值。如果__exit__() 返回 True ,那么异常会被清空,就好像什么都没发生一样,with 语句后面的程序继续在正常执行。

对象节省内存方法

给类添加__slots__属性来极大的减少实例所占的内存。当你定义__slots__后,Python 就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

类中封装属性名

Python 程序员不去依赖语言特性去封装数据,而是通过遵循一定的属性和方法命名规约来达到这个效果。第一个约定是任何以单下划线开头的名字都应该是内部实现,使用下划线开头的约定同样适用于模块名和模块级别函数。第二个约定是使用双下划线开始会导致访问名称变成其他形式, 这种属性通过继承是无法被覆盖的
class B:
    def __init__(self):
        self.__private = 0
    def __private_method(self):
        pass
    def public_method(self):
        pass
        self.__private_method()
类B 中,私有属性会被分别重命名为_B__private 和_B__private_method
class C(B):
    def __init__(self):
        super().__init__()
        self.__private = 1 # Does not override B.__private
    # Does not override B.__private_method()
    def __private_method(self):
        pass
让你的非公共名称以单下划线开头。但是,如果你清楚你的代码会涉及到子类,并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案。
你定义的一个变量和某个保留关键字冲突,这时候可以使用单下划线作为后缀。

可管理的属性

自定义某个属性的一种简单方法是将它定义为一个property
class Person:
    def __init__(self, first_name):
        self.first_name = first_name
    # Getter function
    @property
    def first_name(self):
        return self._first_name
    # Setter function
    @first_name.setter
    def first_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value
    # Deleter function (optional)
    @first_name.deleter
    def first_name(self):
        raise AttributeError("Can't delete attribute")
property 的一个关键特征是它看上去跟普通的attribute 没什么两样,但是访问它的时候会自动触发getter 、setter 和deleter 方法。

创建新的类或实例属性

如果你想创建一个全新的实例属性,可以通过一个描述器类的形式来定义它的功能。一个描述器就是一个实现了三个核心的属性访问操作(get, set, delete) 的类,分别为__get__() 、__set__() 和__delete__() 这三个特殊的方法。这些方法接受一个实例作为输入,之后相应的操作实例底层的字典。
class Integer:
    def __init__(self, name):
        self.name = name
    def __get__(self, instance, cls):
        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
当你这样做后,所有队描述器属性(比如x 或y) 的访问会被__get__() 、__set__()和__delete__() 方法捕获到。
描述器可实现大部分Python 类特性中的底层魔法, 包括@classmethod 、@staticmethod 、@property ,甚至是__slots__特性。描述器的一个比较困惑的地方是它只能在类级别被定义,而不能为每个实例单独定义。
如果你只是想简单的自定义某个类的单个属性访问的话就不用去写描述器了,这种情况下使用property 技术会更加容易,当程序中有很多重复代码的时候描述器就很有用了。
如果一个描述器仅仅只定义了一个__get__() 方法的话,它比通常的具有更弱的绑定。特别地,只有当被访问属性不在实例底层的字典中时__get__() 方法才会被触发。

调用父类方法

为了调用父类(超类) 的一个方法,可以使用super() 函数

在类中定义多个构造器

为了实现多个构造器,你需要使用到类方法。
import time
class Date:
    """ 方法一:使用类方法"""
    # Primary constructor
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    # Alternate constructor
    @classmethod
    def today(cls):
        t = time.localtime()
        return cls(t.tm_year, t.tm_mon, t.tm_mday)

a = Date(2012, 12, 21) # Primary
b = Date.today() # Alternate

让类支持比较操作

装饰器functools.total_ordering(类装饰器) ,你只需定义一个__eq__() 方法,外加其他方法( __lt__, __le__, __gt__, __ge__ ) 中的一个即可。然后装饰器会自动为你填充其它比较方法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值