引言
如何派生内置不可变类型并修改其实例化行为
我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素
实现的结果为:IntTuple([2,-2,‘xj’,[‘x’,‘y’],4]) => (2,4)
如何继承内置tuple 实现IntTuple
# 继承元组类型
class IntTuple(tuple):
# iterable 是什么,是一个可迭代的对象
def __init__(self, iterable):
# 现在是列表的形式
for i in iterable:
if isinstance(i,int) and i>0:
super().__init__(i)
print(i)
intTuple = IntTuple([2, -2, 'xj', ['x', 'y'], 4])
运行结果:
TypeError: object.__init__() takes no parameters
报错了,为什么?因为:如下
self类对象到底是谁创建的呢?
class A(object):
def __new__(cls, *args, **kwargs):
print('A.__new__', cls, args)
return object.__new__(cls)
def __init__(self, a, b):
print('A.__init__', a, b)
a = A(1, 2)
运行结果:
A.__new__ <class '__main__.A'> (1, 2)
A.__init__ 1 2
分析:当创建__new__
这个方法的时候,在创建__init__
方法的时候他的执行顺序是先执行__new__
这个方法,如果把__new__
这个方法中的return这个方法注释掉之后会怎么样
class A(object):
def __new__(cls, *args, **kwargs):
print('A.__new__', cls, args)
# return object.__new__(cls) 为什么要使用return这个返回呢,如果不返回,那__init__就不能被调用
# 当注释了__new__中的return语句的时候,并不会执行__init__方法呢,那是因为其实真正创建的方法其实是__new__,而不是__init__
# __init__其实是真正初始化的方法.真正创建对象出来是__new__这个方法
def __init__(self, a, b):
print('A.__init__', a, b)
a = A(1, 2)
运行结果:
A.__new__ <class '__main__.A'> (1, 2)
分析结果:它只执行了__new__
中的这个方法,它并不会去执行这个__init__
这个方法,这个是为什么呢,那是因为其实__init__
只是个初始化的方法,它真正创建对象的方法不是__init__
,而是__new__
这个方法
元组重写__new__方法
class IntPuter(tuple):
def __new__(cls,iterable):
f = (i for i in iterable if isinstance(i,int) and i>0) # 返回的结果是生成器
return super().__new__(cls,f)
a = [2, -2, 'jr', ['x', 'y'], 4]
da = IntPuter(a)
print(da)
运行结果:
(2, 4)
2.如何为创建大量实例节省内存
在游戏中,定义了玩家类player,每有一个在线玩家,在服务器内则有一个player的实例,当在线人数很多时,将产生大量实例(百万级)
如何降低这些大量实例的内存开销?
解决方案:
定义类的__slots__属性,声明实例有哪些属性(关闭动态绑定)
class Person(object):
__slots__ = ['name', 'age', 'sex','start']
def __init__(self, name, age, sex,start=1):
self.name = name
self.age = age
self.sex = sex
self.start = start
class Date(object):
def __init__(self, name, age, sex,start=1):
self.name = name
self.age = age
self.sex = sex
self.start = start
a = Person('钢铁侠', 18, '男')
a1 = Date('钢铁侠', 18, '男')
print(dir(a))
print(dir(a1))
try:
print(a.__dict__)
except Exception as e: # 尝试输出__dict__属性,发现没有,就无法动态的装配类属性,达到省内存的目的
print(e)
print(a1.__dict__)
运行结果:
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'age', 'name', 'sex', 'start']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'sex', 'start']
'Person' object has no attribute '__dict__'
{'name': '钢铁侠', 'age': 18, 'sex': '男', 'start': 1}
with语句
- 万能的报错器
Exception
- with语句不仅能支持上下文管理协议对象使用。支持本协议的对象
- 当with语句执行时,便执行上下文表达式(context_expr)(一般为某个方法)来获取一个上下文管理起对象,上下文管理器的职责是一个上下文对象,用于在with语句中处理细节
- 一旦获得了上下文对象,就会调用
__enter__
()方法,将完成with语句块执行前的所有准备工作,如果with语句后面跟了as语句,则用__enter__
()方法的返回值来赋值; - 当with语句块结束时,无论是正常结束,还是由于异常,都会调用上下文对象的__exit__()方法,exit()方法有3个参数,如果with语句正常结束,三个参数全部是None;如果发生异常,三个参数的值分别是等于调用
sys.exc_info()
函数返回的三个值:类型(异常类)、值(异常实例)和跟踪记录(traceback),相对应的跟踪记录对象 - 因为上下问管理器主要作用于共享资源,enter()和__exit__()方法基本是完成的是分配和释放资源的底次工作。
当自定义类使用with来管理
class Sample(object):
def speak(self):
print('this is speak')
with Sample() as sample:
sample.speak()
运行结果:
with Sample() as sample:
AttributeError: __enter__
将会抛出个__enter__
这个异常信息,为什么会这样,因为由于自定义类使用with管理上下文的时候,一旦获得上下文对象都会调用__enter__
这个方法,将完成with语句执行前的所有工作,如果with语句后面跟了as语句则用__enter__()
方法来赋值
class Sample(object):
def __enter__(self):
print('开始执行start')
return self
def speak(self):
print('this is speak')
with Sample() as sample:
sample.speak()
运行结果:
with Sample() as sample:
AttributeError: __exit__
当创建出__exit__
时,也抛出了异常信息,这是为什么呢?当with语句块结束时,无论是正常结束,还是由于异常,都会调用上下文对象的__exit__()方法,exit()方法有3个参数,如果with语句正常结束,三个参数全部是None;如果发生异常,三个参数的值分别是等于调用sys.exc_info()
函数返回的三个值:类型(异常类)、值(异常实例)和跟踪记录(traceback),相对应的跟踪记录对象
class Sample(object):
# 获取资源
def __enter__(self):
print('开始执行start')
return self
# 释放资源
def __exit__(self, exc_type, exc_val, exc_tb): # exc_type异常类捕获,exc_val异常值捕获,exc_tb错误信息追踪
print('执行结束')
return self
def speak(self):
print('this is speak')
with Sample() as sample:
sample.speak()
运行结果:
开始执行start
this is speak
执行结束
仔细看执行的顺序,无论代码的顺序是怎么修改,它的代码执行顺序是:
第一步:先执行__enter__
第二步:自己定义的方法
第三步:最后执行__exit__
contextlib简化上下文管理器
import contextlib
@contextlib.contextmanager
def write(filename):
print('open file.txt')
yield {} # yield的上面相当于是__exit__
# yield的下面相当与是__enter__
print('close file.txt')
with write('demo.txt')as f:
print('file 正在读取文件')
运行结果:
open file.txt
file 正在读取文件
close file.txt
使用contextlib
可以更加简易的实现上下文管理文件操作
如何创建可管理的对象属性
在面向对象编程中,我们把方法看做对象的接口。直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如访问属性简洁。
class C(object):
def __init__(self,age):
self.age = age
@property
def x(self):
return self.age > 0
@x.setter
def x(self,x):
if x > 0:
print('我的年龄是{}'.format(x))
else:
raise Exception
a = C(18)
a.x = 20 # 通过属性的方式来创建值
# print(a.x)
运行结果:
我的年龄是20
如何让类支持比较级运算
有时我们希望自定义类的实例间可以使用<, <=, >, >=, ==, !=符号进行比较,我们自定义比较的行为。
比如,有一个矩形,比较两个矩形的实例时,我们希望比较的是面积。
比如,有一个矩形和一个圆形,我们希望比较一个矩形实例和一个圆形实例,
我们希望它们比较的是面积。
__lt__
:比较的是小于
__ge__
:比较的是大于
__eq__
:比较的是等于
class Rect(object):
def __init__(self, w, h):
self.w = w
self.h = h
def __eq__(self, other): # __eq__这个方法是可以判断等于等于,但是并不能判断 >= 或 <=
return self.area() == other.area()
# self其代表着本身
def __lt__(self, other): # othre是什么,是你要跟我比较的字符
return self.area() < other.area() # __lt__这个方法比较的是小于但它能比较大于嘛??
# 答案是可以的,在Python内部,它会自定义做了这么一个判断
def area(self):
return self.w * self.h
def __str__(self): # __str__这个方法是什么时候才调用这个方法 是在print()打印的时候调用
return '%s %s' % (self.w, self.h)
a = Rect(4, 4)
b = Rect(3, 4)
print(a == b)
print(a < b)
print(a != b)
print(a > b)
运行结果:
False
False
True
True
但大家会却不能比较(大于等于)或(小于等于)
class Rect(object):
def __init__(self, w, h):
self.w = w
self.h = h
def __eq__(self, other): # __eq__这个方法是可以判断等于等于,但是并不能判断 >= 或 <=
return self.area() == other.area()
# self其代表着本身
def __lt__(self, other): # othre是什么,是你要跟我比较的字符
return self.area() < other.area() # __lt__这个方法比较的是小于但它能比较大于嘛??
# 答案是可以的,在Python内部,它会自定义做了这么一个判断
def area(self):
return self.w * self.h
def __str__(self): # __str__这个方法是什么时候才调用这个方法 是在print()打印的时候调用
return '%s %s' % (self.w, self.h)
a = Rect(4, 4)
b = Rect(3, 4)
print(a == b)
print(a <= b)
print(a != b)
print(a >= b)
运行结果:
print(a <= b)
TypeError: '<=' not supported between instances of 'Rect' and 'Rect'
# 还是会抛出异常信息
Python中有这么一个库:functools
这个库中有total_ordering
这个方法可以实现上面的>=,<=
的过程
from functools import total_ordering
@total_ordering
class Rect(object):
def __init__(self, w, h):
self.w = w
self.h = h
def __eq__(self, other): # __eq__这个方法是可以判断等于等于,但是并不能判断 >= 或 <=
return self.area() == other.area()
# self其代表着本身
def __lt__(self, other): # othre是什么,是你要跟我比较的字符
return self.area() < other.area() # __lt__这个方法比较的是小于但它能比较大于嘛??
# 答案是可以的,在Python内部,它会自定义做了这么一个判断
def area(self):
return self.w * self.h
def __str__(self): # __str__这个方法是什么时候才调用这个方法 是在print()打印的时候调用
return '%s %s' % (self.w, self.h)
a = Rect(4, 4)
b = Rect(3, 4)
print(a == b)
print(a <= b)
print(a != b)
print(a >= b)
运行结果:
False
False
True
True
类进行比较
from functools import total_ordering
import math
import abc
@total_ordering
class Rect(object):
def __init__(self, w, h):
self.w = w
self.h = h
def __eq__(self, other): # __eq__这个方法是可以判断等于等于,但是并不能判断 >= 或 <=
return self.area() == other.area()
# self其代表着本身
def __lt__(self, other): # othre是什么,是你要跟我比较的字符
return self.area() < other.area() # __lt__这个方法比较的是小于但它能比较大于嘛??
# 答案是可以的,在Python内部,它会自定义做了这么一个判断
def area(self):
return self.w * self.h
def __str__(self): # __str__这个方法是什么时候才调用这个方法 是在print()打印的时候调用
return '%s %s' % (self.w, self.h)
a = Rect(4, 4)
b = Rect(3, 4)
class Circular(object):
def __init__(self,r):
self.r = r
def area(self):
return self.r ** 2 * math.pi
def __eq__(self, other): # __eq__这个方法是可以判断等于等于,但是并不能判断 >= 或 <=
return self.area() == other.area()
# self其代表着本身
def __lt__(self, other): # othre是什么,是你要跟我比较的字符
return self.area() < other.area() # __lt__这个方法比较的是小于但它能比较大于嘛??
# 答案是可以的,在Python内部,它会自定义做了这么一个判断
r = Circular(5)
print(r >= a)
运行结果:
True
但你会发现类比较代码重用性很多,我们可以通过继承自抽象基类去完成这代码的操作
优化后的代码:
from functools import total_ordering
import math
import abc
@total_ordering
class fun(metaclass=abc.ABCMeta): #定义一个抽象基类
def area(self):
pass
def __eq__(self, other):
return self.area() == other.area()
def __lt__(self, other):
return self.area() < other.area()
@total_ordering
class Rect(fun): # 继承自抽象基类
def __init__(self, w, h):
self.w = w
self.h = h
def area(self):
return self.w * self.h
a = Rect(4, 4)
class Circular(fun):
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * math.pi
r = Circular(5)
print(r >= a)
运行结果:
True
通过实例方法调用字符串
他们都有一个获取图形面积的方法,但是方法名字不同,我们可以实现一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应类的接口
三角形
class Triangle:
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def get_area(self):
a, b, c = self.a, self.b, self.c
p = (a + b + c) / 2
return (p * (p - a) * (p - b) * (p - c)) ** 0.5
矩形
class Rectangle:
def __init__(self, a, b):
self.a, self.b = a, b
def getArea(self):
return self.a * self.b
圆
class Circle:
def __init__(self, r):
self.r = r
def area(self):
return self.r ** 2 * 3.14159
getatter
语法:getattr(object, name[, default])
object
:对象
name
:字符串,对象属性
default
:默认返回值,在没有对应的属性将会触发AttributeError
map函数
语法:map(function, iterable, …)
会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
function
:函数
iterable
:一个或多个序列
通过实例方法调用
from lib1 import Triangle
from lib2 import Rectangle
from lib3 import Circle
shap1 = Triangle(3, 4, 5)
shap2 = Rectangle(4, 6)
shap3 = Circle(1)
def demo(shape):
list_data = ['get_area', 'getArea', 'area']
for name in list_data:
f = getattr(shape, name, None)
if f:
return f()
list_demo = [shap1, shap2, shap3]
list_data = list(map(demo, list_demo))
print(list_data)
运行结果:
[6.0, 24, 3.14159]