流畅的python

Python-cookbook之类与对象(1)

2019-05-07  本文已影响0人  洛克黄瓜

创建大量对象时节省内存方法

问题:你的程序要创建大量 可能上百万 的对象,导致占用很大的内存

对于主要是用来当成简单的数据结构的类而言,你可以通过给类添加__slots__属性来极大的减少实例所占的内存,比如:

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

讨论
尽管__slots__看上去是一个很有用的特性,很多时候你还是得减少对它的使用冲动。Python的很多特性都依赖于普通的基于字典的实现,用了__slots__的实例就不用调用__dict__属性了。另外,定义__slots__后的类不再支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__,比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管可以达到这样的目的,但是这个并不是它的初衷。__slots__更多的是用来作为一个内存优化工具。

创建可管理的属性

问题:你想给某个实例attribute增加除访问与修改之外的其他处理逻辑,比如类型检查或合法性验证。

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 not delete attribute')

讨论
一个property属性其实就是一系列相关绑定方法的集合。如果你去查看拥有property的类,就会发现property本身的fget、fset和fdel 属性就是类里面的普通方法。
eg:
In [148]: Person.first_name.fset
Out[148]: <function main.Person.first_name(self, value)>
In [149]: Person.first_name.fget
Out[149]: <function main.Person.first_name(self)>

class Person:
    def __init__(self, first_name):
        self.set_first_name(first_name)

    # Getter function
    def get_first_name(self):
        return self._first_name

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

    # Deleter function(optional)
    def del_first_name(self):
        raise AttributeError('Can not delete attribute')

    # Make a property from existing get/set/del methods
    name = property(get_first_name, set_first_name, del_first_name())

调用父类的方法

问题:你想在子类中调用父类的某个已经被覆盖的方法。

class A:
    def spam(self):
        print('A.spam')


class B(A):
    def spam(self):
        print('B.spam')
        super().spam()  # call parent spam()
class A:
    def __init__(self):
        self.x = 0


class B(A):
    def __init__(self):
        super().__init__()
        self.y = 1
class Proxy:
    def __init__(self, obj):
        self._obj = obj

    # Delegate attribute look up to internal obj
    def __getattr__(self, name):
        return getattr(self._obj, name)

    # Delegate attribute assignment
    def __setattr__(self, name, value):
        if name.startswith('_'):
            super().__setattr__(name, value)
        else:
            setattr(self._obj, name, value)

在上面代码中, __setattr__()的实现包含一个名字检查。如果某个属性名以下划线_开头,就通过super()调用原始的__setattr__(),否则的话就委派给内部的 代理对象self.obj去处理。这看上去有点意思,因为就算没有显式的指明某个类的父类(个人认为这肯定是调用了默认基类object的方法了,其实还是有父类的),super()仍然可以有效的工作。

class Base:
    def __init__(self):
        print('Base.__init__')


class A(Base):
    def __init__(self):
        super().__init__()
        print('A.__init__')


class B(Base):
    def __init__(self):
        super().__init__()
        print('B.__init__')


class C(A, B):
    def __init__(self):
        super().__init__()  # Only one call to super() here
        print('C.__init__')


C.mro()
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, 
# <class '__main__.Base'>, <class 'object'>]

c = C()
# Base.__init__
# B.__init__
# A.__init__
# C.__init__
上一篇下一篇

猜你喜欢

热点阅读