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)