python类和对象的一些注意点


python类和对象的一些注意点


1 创建大量对象的时候 节省内存的方法 __slots__

可以通过给类添加 __slots__ 属性来极大的减少实例所占的内存, 一个更加紧凑的内部表示


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

但是不能再添加新的属性了

需要注意的是:

Python的很多特性都依赖于普通的基于字典的实现。 定义了slots后的类不再支持一些普通类特性了,比如多继承。


2 下划线的使用

单下划线

任何以单下划线_开头的名字都应该是内部实现

双下划线

使用双下划线开始会导致访问名称变成其他形式。 类的继承中私有属性会被分别重命名,这种属性通过继承是无法被覆盖的

大多数而言,你应该让你的非公共名称以单下划线开头。 但是,如果你清楚你的代码会涉及到子类, 并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案

避免关键之冲突的下划线后缀

lambda_ = 2.0

这里不使用单下划线前缀避免误解它的使用初衷 (如使用单下划线前缀的目的 是为了防止命名冲突而不是指明这个属性是私有的)。 通过使用单下划线后缀可以解决这个问题。


3 创建可管理的属性 property装饰器

自定义某个属性的一种简单方法是将它定义为一个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")
        
    p = Person('GuoFei')
    # p.first_name = 42
    print(p.first_name)
    del p.first_name

子类中的覆盖父类属性的管理



class Person:
    def __init__(self, name):
        self.name = name

    # Getter function
    @property
    def name(self):
        return self._name

    # Setter function
    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError('Expected a string')
        self._name = value

    # Deleter function
    @name.deleter
    def name(self):
        raise AttributeError("Can't delete attribute")


class SubPerson(Person):
    @property
    def name(self):
        print('Getting name')
        return super().name

    @name.setter
    def name(self, value):
        print('Setting name to', value)
        super(SubPerson, SubPerson).name.__set__(self, value)

    @name.deleter
    def name(self):
        print('Deleting name')
        super(SubPerson, SubPerson).name.__delete__(self)


1  如果你仅仅只想扩展property的某一个方法那么可以像下面这样写

class SubPerson(Person):
    @Person.name.getter
    def name(self):
        print('Getting name')
        return super().name
        
2  你只想修改setter方法就这么写

class SubPerson(Person):
    @Person.name.setter
    def name(self, value):
        print('Setting name to', value)
        super(SubPerson, SubPerson).name.__set__(self, value)


4 描述器类

创建一个全新的实例属性,可以通过一个描述器类的形式来定义它的功能, 一个描述器 就是一个实现了三个核心的属性访问操作(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]


5 使用延迟计算属性(类装饰器 类似property)

将一个只读属性定义成一个property,并且只在访问的时候才会计算结果。 但是一旦被访问后,你希望结果值被缓存起来,不用每次都去计算。


class lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            return value


import math

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @lazyproperty
    def area(self):
        print('Computing area')
        return math.pi * self.radius ** 2

    @lazyproperty
    def perimeter(self):
        print('Computing perimeter')
        return 2 * math.pi * self.radius



>>> c = Circle(4.0)
>>> c.radius
4.0
>>> c.area
Computing area
50.26548245743669
>>> c.area
50.26548245743669
>>> c.perimeter
Computing perimeter
25.132741228718345
>>> c.perimeter
25.132741228718345

Computing area  Computing perimeter 仅仅出现一次


6 结构化数据的 初始化


基类

class Base(object):
    _fields = []

    def __init__(self, *args, **kwargs):
        if len(args) > len(self._fields):
            raise TypeError("Excepted {} arguments".format(len(self._fields)))

        for name, value in zip(self._fields, args):
            setattr(self, name, value)
        for name in self._fields[len(args):]:
            setattr(self, name, kwargs.pop(name))

        if kwargs:
            raise TypeError("Invalid arguments: {}".format(','.join(kwargs)))

if __name__ == '__main__':
    class Test(Base):
        _fields = ["name", "shares", "price"]  // 自定义的数据结构字段

    s1 = Test("a", 20, 30.1)
    print(s1.__dict__)
    print(vars(s1))


7 定义接口或者抽象基类 确保子类实现了某些特定的方法

使用 abc模块


from abc import ABCMeta, abstractmethod

class IStream(metaclass=ABCMeta):
    @abstractmethod
    def read(self, maxbytes=-1):
        pass

    @abstractmethod
    def write(self, data):
        pass


class SocketStream(IStream):
    def read(self, maxbytes=-1):
        pass

    def write(self, data):
        pass


抽象类的一个特点是它不能直接被实例化

目的就是让别的类继承它并实现特定的抽象方法

主要用途是在代码中检查某些类是否为特定类型,实现了特定接口


8 不使用初始化的__init__, 通过__new__ 构建一个没有属性的对象


from time import localtime

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def today(cls):
        d = cls.__new__(cls)
        t = localtime()
        d.year = t.tm_year
        d.month = t.tm_mon
        d.day = t.tm_mday
        return d


9 状态机制,实现状态对象

    
    class Connection1:
        """新方案——对每个状态定义一个类"""
    
        def __init__(self):
            self.new_state(ClosedConnectionState)
    
        def new_state(self, newstate):
            self._state = newstate
            # Delegate to the state class
    
        def read(self):
            return self._state.read(self)
    
        def write(self, data):
            return self._state.write(self, data)
    
        def open(self):
            return self._state.open(self)
    
        def close(self):
            return self._state.close(self)
    
    
    # Connection state base class
    class ConnectionState:
        @staticmethod
        def read(conn):
            raise NotImplementedError()
    
        @staticmethod
        def write(conn, data):
            raise NotImplementedError()
    
        @staticmethod
        def open(conn):
            raise NotImplementedError()
    
        @staticmethod
        def close(conn):
            raise NotImplementedError()
    
    # Implementation of different states
    class ClosedConnectionState(ConnectionState):
        @staticmethod
        def read(conn):
            raise RuntimeError('Not open')
    
        @staticmethod
        def write(conn, data):
            raise RuntimeError('Not open')
    
        @staticmethod
        def open(conn):
            conn.new_state(OpenConnectionState)
    
        @staticmethod
        def close(conn):
            raise RuntimeError('Already closed')
    
    class OpenConnectionState(ConnectionState):
        @staticmethod
        def read(conn):
            print('reading')
    
        @staticmethod
        def write(conn, data):
            print('writing')
    
        @staticmethod
        def open(conn):
            raise RuntimeError('Already open')
    
        @staticmethod
        def close(conn):
            conn.new_state(ClosedConnectionState)
    
    
    
    # 基类
    c = Connection1()
    print(c._state)  #  默认ClosedConnectionState
    
    c.open()
    print(c._state)  #  OpenConnectionState
    
    
    c.read()
    c.write('hello')
    c.close()
    
    print(c._state) # ClosedConnectionState



10 通过字符串 调用对象方法


import math

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

    def __repr__(self):
        return 'Point({},{})'.format(self.x, self.y)

    def distance(self, x, y):
        return math.hypot(self.x - x, self.y - y)

p = Point(2.01, 3.21)



getattr

d = getattr(p, 'distance')(0, 0)  
print(d)

operator.methodcaller

import operator
ret = operator.methodcaller('distance', 0, 0)(p)
print(ret)
Buy me a 肥仔水!