1. 派生内置不可变类型并修改实例化行为
这里举一个例子:
我们要得到一个类,继承自tuple,但是这个特殊的tuple只能容纳正数。
### 派生内置不可变类型,修改其实例化行为
### 解决方法:重写__new__方法
class IntTuple(tuple) :
def __new__(clazz , iterable) :
g = (x for x in iterable if isinstance(x , int) and x > 0 )
return super(IntTuple , clazz).__new__(clazz , g)
t = IntTuple([-1,1,'abc' , ['666' , 999] , 3])
print(t) # (1, 3)
2. 为大量实例对象节省内存
# 如何为大量实例节省内存空间
from pydoc import plain
class Player():
# 关闭了动态绑定属性的特点
# 动态绑定属性的特点以牺牲内存为代价
# 方法就是定义 __slots__属性
__slots__ = ['uid' , 'name' , 'stat' , 'level']
def __init__(self , uid , name , status = 0 , level = 1) :
self.uid = uid ;
self.name = name
self.stat = status
self.level = level
p = Player('001' , 'hs')
p.x = 123 # 定义了__slots__,这么写就不再被允许了
print(p.x)
3. 拥有上下文管理
这段是python2的代码,勉强可以运行。但是命令无法到达远程终端。我尽力了。
### 让自己手写额实例能像with语句打开文件之后自动关闭文件一样神奇
### 方式:需要定义实例的 __enter__ , __exit__ 方法
### 它们分别在with开始和结束的时候被调用
from telnetlib import Telnet
from sys import stdin , stdout
from collections import deque
import time
class TelnetClient() :
def __init__(self,addr,port = 22) :
self.addr = addr
self.port = port
self.tn = None
def start(self) :
# user
t = self.tn.read_until('login: ',timeout=10)
stdout.write(t)
user = stdin.readline()
self.tn.write(user)
# password
self.tn.read_until('Password: ',timeout=10)
if t.startswith(user[:-1]) :
t = t[len(user) + 1 : ]
stdout.write(t)
self.tn.write(stdin.readline())
# time.sleep(2)
# t = self.tn.read_until("# ")
# stdout.write(t)
while True :
uinput = stdin.readline()
if not uinput :
break
self.history.append(uinput)
self.tn.write(uinput)
t = self.tn.read_until('# ')
stdout.write(t[len(uinput) + 1 :])
def __enter__(self) :
self.tn = Telnet(self.addr , self.port)
self.history = deque()
return self
def __exit__(self,exc_type , exc_val , exc_tb) :
self.tn.close()
self.tn = None
with open(self.addr + '_history.txt' , 'w') as f :
f.writelines(self.history)
# client = TelnetClient('127.0.0.1')
# client.start()
# client.cleanup()
with TelnetClient('127.0.0.1') as t:
t.start()
4. 创建可管理的对象属性
这里注意,为类的属性起名称的时候,务必使用下划线打头。
否则会引起递归调用错误。
# 拥有python访问属性的快捷,又有java访问属性的安全
from math import pi
class Circle():
def __init__(self, radius):
self._radius = radius
def getRadius(self):
return self._radius
def setRadius(self, value):
if not isinstance(value, (int, float)):
raise ValueError("wrong type.")
self._radius = value
def getArea(self):
return self._radius ** 2 * pi
radius = property(getRadius, setRadius)
c = Circle(3.2)
print(c.radius)
c.radius='abc'
print(c.radius)
5. 自定义类支持比较操作
# 让类支持比较操作
# 方法1:重载__lt__ ,__le__, __ge__, __gt__, __eq__, __ne__
# 方法2:通过total_ordering简化过程
from functools import total_ordering
from abc import ABCMeta , abstractmethod
from math import pi
@total_ordering
class Shape:
@abstractmethod # 将area声明成抽象方法
def area(self) :
pass
def __gt__(self,obj) :
if not isinstance(obj , Shape) :
raise TypeError('obj is not a Shape')
return self.area() > obj.area()
def __eq__(self , obj) :
if not isinstance(obj , Shape) :
raise TypeError('obj is not a Shape')
return self.area() == obj.area()
class Rectangle(Shape):
def __init__(self,w,h) :
self._w = w
self._h = h
def area(self):
return self._w * self._h
class Circle(Shape):
def __init__(self,r):
self._r = r
def area(self):
return self._r **2 * pi
rect1 = Rectangle(5,3)
rect2 = Rectangle(4,4)
c1 = Circle(5)
print(rect1 > rect2) # rect1.__gt__(rect2)
print(rect1 < rect2)
print(c1 > rect2)
6. 对实例属性进行类型检查
# 使用描述符对实例对象进行类型检查
# 换言之,使得python可以具有静态类型语言的优势
# 方法:使用描述符来实现需要检查的属性
'''
分别实现 __get__ __set__ __delete__ 方法
在__set__内使用isinstance函数做类型检查
'''
class Attr:
def __init__(self , name , type_) :
self.name = name
self.type_ = type_ # 记录属性名称以及其类型
def __get__(self,instance , cls) :
return instance.__dict__[self.name] # 把self.name 属性赋给instance
def __set__(self,instance,value) :
if not isinstance(value , self.type_) : # 检查属性值和其类型是否一致
raise TypeError('expected a(n) %s' % self.type_)
instance.__dict__[self.name] = value
def __delete__(self,instance) :
del instance.__dict__[self.name]
class Person :
name = Attr('name' , str)
age = Attr('age' , int)
height = Attr('height' , float)
p = Person()
p.name = 'Bob'
print(p.name)
p.age = '17'
print(p.age)
7. weakref消除循环引用,从而管理内存
弱引用可以访问一个对象,但是不会增加引用计数
当引用计数到0,便立即回收
这里的例子中,data掌握node的弱引用,没有增加node的引用计数,从而消除了循环引用的问题。
import weakref
class Data :
def __init__(self,value , owner) :
self.owner = weakref.ref(owner)
# self.owner = owner
self.value = value
def __str__(self) :
return '%s \'s data , value is %s' % (self.owner , self.value)
def __del__(self) :
print( 'in Data.__del__' )
class Node :
def __init__(self , value) :
self.data = Data(value , self)
def __del__(self) :
print('in Node.__del__')
node = Node(100)
del node
input("wait...")
8. methodcaller建造方法柄,通过传入对象来实现调用
# 使用实例方法的名字的字符串来调用方法
from operator import methodcaller
from math import pi
class Circle :
def __init__(self , _r) :
self._r = _r
self._area = pi * _r**2
def area(self):
return self._area
class Triangle :
def __init__(self , _a , _h) :
self._a = _a
self._h = _h
self._area = _a * _h / 2
def get_area(self):
return self._area
class Rectangle :
def __init__(self , _a ,_b) :
self._a = _a
self._h = _b
self._area = _a * _b
def Area(self):
return self._area
circle_measure = methodcaller('area')
print(circle_measure(Circle(2)))
triangle_measure = methodcaller('get_area')
print(triangle_measure(Triangle(2,3)))
rectangle_measure = methodcaller('Area')
print(rectangle_measure(Rectangle(2,3)))