Python Cookbook学习记录

8.类与对象
8.1修改实例变量的字符串表示

'''
对于__repr__()标准做法是让他产生的字符串文本能够满足eval(repr(x))=x
若不想这么做,通常让他产生一段有意义的文本,并且以<和>括起来
'''

class Pair(object):
    def __init__(self, x, y):
        self.x=x
        self.y=y

    def __str__(self):
        return '({0.x!s}, {0.y!s})'.format(self)

    #特殊方法__repr__()返回的是实例的代码表示,通常可以用它返回的字符串
    #文本来重新创建这个实例既满足obj=eval(repr(obj))
    #repr在交互式解释环境时会返回字符串
    def __repr__(self):
        #格式化代码0.x用来指代参数0的x属性因此在下面的函数中0实际代表着self
        return 'Pair({0.x!r}, {0.y!r})'.format(self)
#str
print(Pair(3, 4))

p = Pair(3, 4)
#特殊的格式化代码!r表示应该使用__repr__()的输出而不是默认的__str__()的输出
print('p is {0!r}'.format(p))
print('p is {0}'.format(p))

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

'''
要定义字符串的输出格式,可以在类中定义___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)
print(format(d))
print(format(d, 'mdy'))
print('The date is {:ymd}'.format(d))
print('The date is {:mdy}'.format(d))

8.3让对象支持上下文管理

'''
要让对象能够兼容with语句,需要实现__enter__()和__exit__()方法
当遇到with语句时,__enter__()方法首先被触发执行。__enter__()的返回值被
放置在由as限定的变量中。之后开始执行with代码块中的语句,最后__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 = AF_INET
        self.type = SOCK_STREAM
        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

from functools import partial

conn = LazyConnection(('www.python.org', 80))
with conn as s:
    s.send(b'GET /index.html HTTP/1.0\r\n')
    s.send(b'Host: www.python.org\r\n')
    s.send(b'\r\n')
    resp = b''.join(iter(partial(s.recv, 8192), b''))

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

'''
在类中添加__slots__属性
当定义了__slots__属性,python不再让每个实例都创建一个__dict__字典,
现在的实例是围绕着一个固定长度的小型数组来构建的,这和一个元祖或列表很相似
在__slots__中列出的属性名会在内部映射到这个数组的特定索引上,副作用是没法再
对实例添加任何型的属性,限制为只允许使用__slots__属性中列出的属性名
'''

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

8.5把名称封装到类中

class A:
    def __init__(self):
        #以特定的命名规则来限制访问
        #内部访问,子类也能访问
        self._internal = 0
        self.public = 1

    def public_method(self):
        pass
    def _internal_method(self):
        pass

class B:
    def __init__(self):
        #以双下划线打头的名称会导致出现名称重整的行为,具体就是__private
        #会被重名为_B__private,__private_method会被重命名为_B__private_method
        #双下划线规定私有,子类也不能访问即不能通过继承而覆盖
        self.__private = 0

    def __private_method(self):
        pass
    def public_method(self):
        pass

class C(B):
    def __init__(self):
        super(C, self).__init__()
        self.__private = 1 #并没有重写B中的__private, 这是_C__private

    def __private_method(self):
        pass

#定义一个变量,可能会和保留字冲突,应该在变量名后加下划线
lambda_ = 2

8.6创建可管理的属性

#要自定义对属性的访问,一种简单的方式是将定义为property
#即把类中的方法当做一种属性来使用
class Person:
    def __init__(self, first_name):
        #调用setter方法
        #property会在设置属性时进行类型检查,所以下面会调用setter
        self.first_name = first_name
    #getter函数,获取属性
    @property
    def first_name(self):
        return self._first_name
    #除非first_name已经通过@property方式定义为了property属性
    #否则是不能定义@first_name.setter和@first_name.deleter装饰器的
    @first_name.setter
    def first_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value

    @first_name.deleter
    def first_name(self):
        raise AttributeError("Can't delete attribute")

#property看起来就像一个普通的属性,但是根据访问它的方式不同会自动
#触发getter、setter和deleter方法
a = Person('Guido')
print(a.first_name)
#a.first_name=43 Error
#del a.first_name Error


#已经存在的get和set方法,也可以将它们定义为property
class Person1:
    def __init__(self, first_name):
        self.set_first_name(first_name)

    def get_first_name(self):
        return self._first_name

    def set_first_name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._first_name = value

    def delete_first_name(self):
        raise AttributeError("Can't delete attribute")

    name = property(get_first_name, set_first_name, delete_first_name)

per = Person1('Guido')
print(per.name)
per.name = 'James'
print(per.name)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值