简单例子1
内建函数setattr() getattr() hasattr() 即使类中没有setattr 和 getattr魔术方法也不会报错 不同于len(A()), 如果类中没有len方法则会报错 setattr时,第2个位置的参数,可以是属性,也可以是方法,例如增加x属性,也可以是方法x。
class Point:
def init(self, x, y):
self.x = x
self.y = ydef str(self):
return "Point({}, {})".format(self.x, self.y)def show(self):
print(self)p = Point(1, 2)
动态增加或修改
print('*******')
print(setattr(p, 'x', 99)) # None
print(setattr(p, 'z', 100)) # None
print(p.dict)获取
print(getattr(p, 'y')) # 2
print(getattr(p, 'q', None)) # None
print(getattr(p, 'dict')){'x': 99, 'y': 2, 'z': 100},属性获取
print(p.dict)
{'x': 99, 'y': 2, 'z': 100}, 实例字典获取
判断
print(hasattr(p, 'a')) # False
print(hasattr(p, 'x')) # True
# 综合例子
通过实例判断,获取属性
if hasattr(p, 'show'):
getattr(p, 'show')() # Point(99, 2)
getattr返回一个绑定方法show,调用show方法。
通过 实例 判断,动态增加属性(用的少)
if not hasattr(p, 'sub'):
setattr(p, 'sub', lambda point1, point2: Point(point1.x - point2.x, point1.y - point2.y))
print(p.sub(Point(1, 2, ), Point(3, 3))) # Point(-2, -1)
通过 类 判断,动态增加类属性
if not hasattr(Point, 'add'):
setattr(Point, 'add', lambda point1, point2: Point(point1.x + point2.x, point1.y + point2.y))
print(p.dict) # 实例字典中有sub方法
print(Point.dict) # 类字典中有add方法,没有sub方法
# 只用来测试(不优雅)
class Point:
def init(self, x, y):
self.x = x
self.y = y
def str(self):
return "Point({}, {})".format(self.x, self.y)
def show(self):
print(self.x, self.y)
def len(self):
return 10
p = Point(1, 2)
感觉这两个差不多
print(Point.dict) # 类字典。魔术方法,存在类字典中
print(dir(Point)) # 父类和实例的各种方法及属性,包括魔术方法,不包括x, y, z ,有序列表
print(p.dict) # 只有x,y,实例字典只存放init中的内容
print(dir(p)) # 最全,父类和实例的各种方法及属性,包括魔术方法,包括x, y, z, 有序列表
print(p.dir()) # 同dir(p), 无序列表
print(Point.dir(p)) # 同dir(p), 无序列表。类调用方法(魔术和普通),都不会注入第一参数
p.dict['x'] = 99 # 相当于调用setattr方法
print(p.dict)
p.z = 100 # 相当于调用setattr方法
print(p.dict) # 增加一个z
print(p.z) # 相当于调用getattr方法
-
魔术方法setattr getattr delattr getattribute
setattr
-
调用setattr 的情景
init 函数中,self.x = x
在类的外面 obj.x = 50
在类的外面 setattr(obj, 'x', 50)
不会调用setattr 的情景
obj.dict['x'] = 50
setattr 魔术方法
-
# 这个方法可以拦截对实例属性的增加,修改操作,如果设置生效,实例的字典为空 # 实例通过.点号设置属性,例如设置self.x = x属性,就会调用
class Point:
z = 6
def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y
def setattr(self, key, value):
self.dict.setitem(key, value)
等价于 self.dict[key] = value
p = Point(1, 2)
p.x = 20
print(p.dict) # {'x': 20, 'y': 2}因为初始化时也会调用2次
setattr
setattr , 但是如果实例的属性要加到实例的dict中, 需要自己手动完成, 如果不手动添加,则默认dict是清空的
如果只增加setattr方法,则实例字典会被清空
可以通过实例字典设置属性,但是取不出来属性,即无法使用obj.x
class Point:
z = 6
def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y
def setattr(self, key, value):
print("setattr {} = {}".format(key, value))
p = Point(1, 2)
print(Point.dict) # 有值
print(p.dict) # 如果只增加setattr方法,实例字典被空的
因为实例字典被清空了,而且也不会调用内键函数getattr(),所以报错
print(p.x) # 报错
print(p.y) # 报错
print(p.z) # 6
p.x = 50 # 还会调用setattr
print(p.x) # 刚设置完x属性,但还是取不到x属性,因为实例字典还是空的
print(p.dict) # {}
p.dict['x'] = 60 # 只有在实例的字典中添加x属性,才可以取到x属性,但是不会调用setattr
print(p.dict) # 字典中有值{'x': 60}
print(p.x) # 60
getattr
# 如果只增加getattr方法和原来没有什么变化,相当于没增加,一般不这么用
class Point:
z = 6
def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y
def getattr(self, item):
return "missing {}".format(item)
p = Point(1, 2)
print(Point.dict) # 有值
print(p.dict) # 如果只实现了getattr方法,实例字典中有值{'x': 1, 'y': 2}
print(p.x) # 1
print(p.y) # 2
print(p.z) # 6
p.m = 50 # 因为没有实现setattr方法,所以调用的是内键函数setattr()
print(p.m) # 50
print(p.dict) # 实例字典中有值{'x': 1, 'y': 2, 'm': 50}
p.dict['x'] = 60 # 只有在实例的字典中添加x属性,才可以取到x属性,但是不会调用setattr
print(p.dict) # 实例字典中有值x被改成{'x': 60, 'y': 2}
print(p.x) # 60
# getattr 魔术方法
查找属性顺序
instance.dict -> instance.class -> 继承类 —> object字典
class Base(object):
n = 0
z = 200
class Point(Base):
z = 6
x = 100
def init(self, x, y):
self.x = x
self.y = y
def getattr(self, item):
return "missing {}".format(item)
p = Point(1, 2)
print(Point.dict)
print(p.dict)
虽然父类没有实现setattr
但是object中实现了setattr
print(p.x) # 1
print(p.y) # 2
print(p.z) # 6
print(p.n) # 0
print(p.t) # missing 找了一圈找不到就会调用 getattr方法,
setattr getattr综合应用的例
-
大多数情况都是这两种魔术方法同时使用
# 其实不用实现这两个魔术方法,就可以满足开发需求
lass Point:def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y
p = Point(1, 2)
print(p.x) # 1
print(p.y) # 2
p.z = 9
print(p.z) # 9
# 如果想使用__setattr__ 和 __getattr__,则正常使用的例子
class Point:
z = 6
def __init__(self, x, y):
self.x = x # 这时会调用__setattr__
self.y = y
def __getattr__(self, item):
return item
def __setattr__(self, key, value):
# 直接操作字典不会递归
self.__dict__[key] = value
p = Point(1, 2)
print(Point.__dict__) # 有值
print(p.__dict__) # 有值
print(p.x) # 1
print(p.y) # 2
print(p.z) # 6
p.x = 50 # 还会调用因为实现了__setattr__,所以会调用__setattr__,内部打印字符串就可以看到
print(p.x) # 50
print(p.__dict__) # 有值
p.__dict__['x'] = 60 # 实例的字典中添加x属性,但是不会调用__setattr__,内部打印字符串就可以看到
print(p.__dict__) # 有值
print(p.x) # 60
# 内键函数与魔术方法的区别例子
setattr()函数,当类中实现了setattr方法时,会走类中的这个方法
getattr()函数,当类中实现了getattr方法时,并且实例的字典中没有要获取的属性时,才会走类中的这个方法
class Point:
z = 6
def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y
def getattr(self, item):
print('111111')
return 'item: {}'.format(item)
def setattr(self, key, value):
直接操作字典不会递归
print('222222')
self.dict[key] = value
p = Point(1, 2)
p.x = 50 # 调用setattr
print(p.x) # 实例的字典中取到了50,没调用getattr
print(p.m) # 实例的字典中没有m属性,才会调用getattr
setattr(p, 'n', 99) # 调用setattr
print(p.n) # 取实例的字典中n属性,没调用getattr
print(p.m) # 实例的字典中没有m属性,才会调用getattr
print(getattr(p, 'n')) # 从字典中取
print(getattr(p, 't')) # 调用getattr
# 测试用例,正常代码不这么写
class Point:
z = 6
def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y
def getattr(self, item):
return "missing {}".format(item)
def setattr(self, key, value):
print("setattr {} = {}".format(key, value))
p = Point(1, 2)
print(Point.dict) # 有值
print(p.dict) # 空的
因为实例字典被清空了,但是类字典没有被清空,所以取不到x,y,但是能取到z
print(p.x) # missing x
print(p.y) # missing t
print(p.z) # 6
p.x = 50 # 还会调用setattr
print(p.x) # 刚设置完x属性,但还是取不到x属性,因为实例字典还是空的
print(p.dict) # {}
p.dict['x'] = 60 # 只有在实例的字典中添加x属性,才可以取到x属性,但是不会调用setattr
print(p.dict) # 字典中有值{'x': 60}
print(p.x) # 60
class Base:
n = 0 class Point(Base):
z = 6
d = {}
def init(self, x, y):
self.x = x # 这时会调用setattr
setattr(self, 'y', y)
self.dict['a'] = 5
def getattr(self, item):
print("missing {}".format(item))
return self.d[item]
def setattr(self, key, value):
print("setattr {} = {}".format(key, value))
self.d[key] = value
p = Point(1, 2)
print('+++++++++++++')
print(Point.dict)
print(p.dict)
print(p.x) # missing x 当找到类的字典时,'d'下面有'x', 但是外面没有'x', d': {'x': 1, 'y': 2}
print(p.z) # 6
print(p.n) # 0
print(p.t) # missing t
print(p.a) # 5
delattr( 了解)
class Point:
z = 6
def init(self, x, y):
self.x = x
self.y = y
def delattr(self, item):
return print('Can not del {}'.format(item))
p = Point(1, 2)
del p.x
p.z = 15
del p.z
不能删除了
getattribute(了解)
实例的所有属性访问,第一个都会调用这个方法,它阻止了属性的查找顺序,
该方法应该返回计算后的值,或者抛出一个Attribute Error异常
它的return值将作为属性查找的结果
如果抛出AttributeError, 则会直接调用 getattr方法,因为属性没有找到
属性查找顺序
# instance.getattribute ->instance.dict -> instance.class -> 祖类 —> object字典
使用方法
# getattribute方法中为了避免出现无限递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如:object.getattribute(self, name),
一般不要使用这个方法
class Point(Base):
z = 6
def init(self, x, y):
self.x = x
self.y = y
def getattr(self, item):
return("missing {}".format(item))
def getattribute(self, item):
return object.__getattribute(self, item)
return super().getattribute(item)
p = Point(1, 2)
print('+++++++++++++')
print(Point.dict)
print(p.dict)
print(p.x) # 1
print(p.z) # 6
print(p.n) # 0
print(p.t) # missing t
增加子类功能的3种方法
1、直接继承然后修改,2、给子类增加一个装饰器, 3、Mixin Mixin就是其他类混合进来,同时带来了类的属性和方法,本质上是多继承,是一种组合的设计模式。和装饰器比较,两者的装饰效果一样,但是Mixin是类,可以继承。 Mixin类的使用原则:
Mixin类中不应该显示的出现init方法
Mixin类通常不能独立工作,因为它是准备混入别的类中,实现部分功能
-
Mixin类的祖先类也应该是Mixin类
# 直接继承
class Document: #因为有print方法未实现,所以称为抽象基类
def init(self, content):
self.content = contentdef print(self):
raise NotImplementedError
class Word(Document):
def print(self):
print(self.content, 'word style')
class Pdf(Document):
def print(self):
print(self.content, 'pdf style')
class Test(Document): #在python中,这个子类可以不实现父类中的print方法,
# 但是在其他语言中,如果继承自父类,但是父类的抽象方法,未能全部实现,
# 则这个子类也属于抽象类,抽象类不可以实例化
pass
w = Word('tutu')
w.print()
#装饰器
class Document:
def __init__(self, content):
self.content = content
class Word(Document):
pass
# 装饰器1
def printable(cls):
def wrapper(content):
cls.print = lambda: print(content)
return cls
return wrapper
# 装饰器2
def printable(cls):
def wrapper(content):
def fn():
print(content)
cls.print = fn
return cls
return wrapper
# 装饰器3 **********************************
def printable(cls):
def fn(self): # 这3行等价于 cls.print = lambda self: print(self.content)
print(self.content)
cls.print = fn
return cls
# 装饰器4,注意使用print时,不要出现递归
def printable(cls):
def _print(self): # 写在类的外面的,但是需要给类添加的方法,第一个参数必须为self
print(self.content)
cls.print = _print
return cls
@printable #PrintableWord = printable(PrintableWord)
class PrintableWord(Word):pass
p = PrintableWord('abs')
p.print()
# Mixin,多继承
# PrintableMixin一般值需要里面的print方法,里面一般不写__init__
class Document:
def __init__(self, content):
self.content = content
class Word(Document):
pass
class PrintableMixin:
def print(self): #这个两个print不会出现递归,因为第一个print相当于有一个隐藏的名字,
# 只能通过这两种方法调用,PrintableMixin.print(instance) p.print()
print(self.content)
class PrintableWord(PrintableMixin, Word):pass
p = PrintableWord('abs')
p.print()
#print(PrintableMixin.__dict__)
# Mixin增强
class Document:
def __init__(self, content):
self.content = content
class Word(Document):
pass
# Mixin功能,多继承
class PrintableMixin:
def print(self):
print(self.content)
class SuperPrintableMixin(PrintableMixin):
def print(self):
print('增强之前打印:', self.content)
super().print()
print('增强之后打印:')
class PrintableWord(SuperPrintableMixin, Word): pass
p = PrintableWord('abs')
p.print()
python2.7与python本质区别(了解)
python2.7中与python3的不同: 都不同python3 type(A), dir(A) a.class
dir() 函数的用法 dir(A) 收集对象的属性
模块化
模块化是组织代码的方式,封装代码,封装就是边界
在其他编程语言中,库、包、模块是同一种概念,是代码组织方式
在python中:
模块module:python文件
包package:是指模块组织在一起的,和包同名目录及其相关文件
导入语法
-
import 只能加模块名
# 完全限定名称访问path
import os.path # 导入os.path, os加入当前名字空间
print(dir()) # 里面只有os,没有os.path -
import as
import os.path as osp # 导入os.path并赋值给osp
print(dir()) # 里面只有osp -
局部导入
def testimport():
import os.path
print(dir()) # os存在里面
testimport()
print(globals().keys()) # os不存在里面
-
总结
导入顶级模块,其名称会加入到本地名词空间中,并绑定到其模块对象
导入非顶级模块,只将其顶级模块名称导入到本地名词空间。导入的模块必须使用完全限定名称来访问。
如果使用了as, as后的名称直接绑定到导入的模块对象,并将该名称加入到本地名词空间
-
其他例子:
from functools import wraps as wr, partial
print(dir()) # [..., 'wr', 'partial']四种方式获得同一个对象的 exists
import os
print(os.path.exists)
print(exists)
print(os.path.dict['exists']) # 字符串,os.path被导入之后,可以取dict
print(getattr(os.path, 'exists')) # 字符串, module对象也可以使用getattr来获得属性 -
from 必须加模块 import 类名,函数名,模块名
from pathlib import Path # 导入类Path
print(Path, id(Path))
import pathlib as p1
print(p1.Path, id(p1.Path)) # p1.Path 与 Path 是同一个对象,id相同
自定义模块
模块名:由字母数字下划线组成,不能以数字开头,不能有中文
不要使用系统模块名,以避免冲突,通常模块名为全小写,下划线来分割。
# test1.py
class A:
def showmodule(self):
print(1, self.module, self)
print(2, self.dict)
print(3, self.class.dict)
print(4, self.class.name)
a = A()
a.showmodule() # self.module = 'main'
test2.py
import test1
a = test1.A()
a.showmodule() # self.module = 'test1'
test3.py
from test1 import A as cls
a = cls()
a.showmodule() # # self.module = 'test1'
模块搜索顺序
会从sys.path中,从前到后依此查找,并不搜索这些目录的子目录
路径可以是字典、zip文件、egg文件。egg文件是由setuptools库创建的包, 第三方库常用的格式。zip文件是添加了元数据(版本号,依赖项等)信息的文件
print(*sys.path, sep='\n')
sys.path路径搜索顺序为
程序主目录,程序运行主程序脚本所在的目录文件
PYTHONPATH目录,环境变量PYTHONPATH设置的目录
标准库目录,python自带的库模块所在的目录
第3方库
模块会出现重复导入嘛?
模块不会存在重复导入的现象,所有加载的模块都会记录在sys.modules 中,
sys.modules是存储已经加载所有模块的字典,里面包含os, os.path
# test1.py
class A:
def showmodule(self):
print(1, self.module, self)
print(2, self.dict)
print(3, self.class.dict)
print(4, self.class.name)
a = A()
a.showmodule()
test2.py
import test1
print('*****')
import test1 # 只能看到一次1,2,3, 4的打印,只有第一次导入打印了,
print(sys.modules) # 可以看到test1在里面了。
name main
解释器初始化时,会初始化sys.modules字典,然后加载builtins模块、main模块、sys模块,及sys.path
name
每个模块都有这个特殊变量来存储当前模块的名称,如果不指定,则默认为源代码文件名,如果是包则有限定名。import导入模块,name默认是模块名。
当从标准输入(命令行方式巧代码)、运行脚本($ python test.py)、交互式读取时,这3种情况都会将name设置为main,模块的顶层代码在main,这个作用域中执行
if name == 'main':
print('in main')
else:
print('in imported module')
if name == 'main':作用
对于非主模块,测试本模块内的函数、类
-
避免主模块变更的副作用
顶层代码没有封装,主模块使用时没有问题。但是,一旦有了
新的主模块,旧的主模块成了被导入模块,由于原来代码没有封装,一并执行了
模块属性
file 源文件绝对路径,字符串
name 模块名
package 如果模块是包,同name; 否则可以设置为顶级模块的空字符串
spec 显示模块规范
cached 编译后的字节码文件路径,字符串
在m1文件中导入m2,并且查看m2属性的两种方法
import m1.m2 as m2 # m1下面同时存在m2模块和m2包,找到的是m2包
print(dir(m2)) # 获得m2的属性名,返回列表,如果在m2里面的init文件中,加入属性B,则可以看到
print('************')
for name in dir(m2):
print(getattr(m2, name))
m2模块相当于一个字典,从字典中获得name属性。例如:
name doc file等
等价于
print('***********')
for k, v in m2.dict.items():
print(v)
问题:如果m2中有变量A,为什么dir(m2),看不到A,如果在m2中输入dir(),就可以看到A?因为先找到的是m2包
包
- 总结:
# 如果想在目录下面写代码,需要写入目录下的初始化文件中
当目录作为模块使用,则会执行初始化文件中的代码
模块不是包,但是,包是一种特殊的模块
模块就是命名空间,其内部的顶层标识符,都是它的属性,可以通过dict或者dir(module)查看。
-
模块的加载与加入到命名空间的对比
import sys
import m.m1.m2
print(dir()) # 只有'm'
print(sorted(filter(lambda x: x.startswith('m'), sys.modules.keys())))'m', 'm.m1', 'm.m1.m2'都在,依次加载m,m1,m2的初始化文件
from os import stat
print(dir()) # 没有osprint(os.path()) # 为什么用不了?os没在当前的命名空间,虽然os已经被加载过了
改成
import os.stat
print(os.path) # 可以使用因为os,加入到了当前命名空间了 -
绝对导入与相对导入
在包内部各个模块之间相互调用,建议使用相对导入
在包外部使用,包内部模块,建议使用绝对导入
-
绝对导入:import 或者from语句后面没有. 绝对导入的搜索顺序是,
先到sys.modules中查看是否已经被加载,被加载直接取出,如果没有被加载,则按照sys.path中的顺序搜索,然后加载到sys.modules模块中,sys.path的搜索顺序是当前运行模块的根路径,PYTHONPATH路径,标准库和第三方库。
-
相对导入:
只能from语句后面有.
1个点表示当前目录内
2个点表示上一级目录
3点表示上上级目录
一旦一个模块中使用相对导入,就不可以作为主模块运行了
相对导入是为了保证包内部资源的相互引用,而不是为了直接运行,正确的使用方式是在顶层模块中使用这些包。
-
-
- 与all
在模块m11中定义普通变量、保护变量、私有变量、特殊变量:
A = 5
_B = 6
__C = 7
my = 8在模块m1中导入模块m11
import m11
print(m11.A, m11._B, m11.__C, m11.my) # 没有被隐藏都可以访问也就是说模块内所有变量都不做特殊处理
如果导入时使用*:
# 在m1模块中写入代码,并且运行m1
from m11 import * # 问题:问什么使用.m11就不可以呢?
print(dir()) # 只能看到普通变量A,带下划线的都看不到了如果在模块m11中加入all, 则all中有什么,命名空间就有什么变量
all是一个列表,里面元素是字符串,每个元素只能是当前模块中的属性名。
all = ['A', '_B']
A = 5
_B = 6
__C = 7
my = 8在m1中运行
from m11 import *
print(dir()) # ['A', '_B'] -
例子:从顶层模块访问包中属性的方法
如果使用了*, 就尽量使用all, 避免导入过多的模块,而产生冲突
一般使用from module import name1, name2
已知:
# m 的init.py中写入
print(name)
x = 5m1文件中写入
print(name)
y = 5
问题:顶层模块test.py如何访问到m1中的y?
# 方法1
在test文件中写入
from m import m1
print(m1.y)
# 方法2(不可以因为m.m1没在命名空间,容易错的导入)
import m
print(m.m1.y)
# 方法3
# 在m的__init__文件中写入 ,__all__ = ['x', 'm1'], __all__中包含的字符串名字是__init__.py中的属性名,和m包下面的模块名m1.
from m import *
print(dir()) # ['x', 'm1']
print(m1.y)
# 方法4
# 在m的__init__文件中写入
# from . import m1
from m import * # __init__.py中有什么变量,就导入了什么变量
print(m1.y)
-
模块变量的修改
模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改会影响所有的使用该模块使用者,特别是常量,引用类型的还好。
另外可以通过猴子补丁的方式修改模块的变量、类、函数。
打包分发
使用setup.py打包
在项目根目录下建立一个setup.py文件(和m包同级),在里面填写脚本
from distutils.core import setup
setup(name='m',
version='1.0',
description='Python test m',
author='JW',
author_email='',
url='',
packages=['m', 'm.m1', 'm.m1.m2'],
)
packages里面填写,所有包名
创建源代码分发包
# 在项目根目录下生成dist文件里面有m-1.0.tar.gz
$ python setup.py sdist
安装写好的包,方法1
$ pip install m-1.0.tar.gz
安装写好的包,方法2
先解压,里面含有setup.py文件
$ python setup.py install
对于需要编译的语言,需要先编译再安装
$ python setup.py build
会在根目录下生成一个build文件夹,在lib下是编译好的文件
setup.py生成各种压缩包的命令
python setup.py bdist --format=rpm
python setup.py bdist --format=egg
python setup.py bdist_wheel
插件化开发
参考笔记10模块化03插件化开发.pdf
面向对象补充
未实现和未实现异常
# NotImplemented, 是NotImplementedError实例,是个单值,类似与None
NotImplementedError,是一个类
print(type(NotImplemented)) #
print(type(NotImplementedError)) #
slots魔术方法
# slots内存不够用时使用,需要很多实例,例如:点类,适用属性和值都简单的类,
使用slots
不可以动态增加属性
实例没有dict属性了
不影响继承实例的字典
class A:
x = 1
slots = ('y', 'z') # 一般用元组
slot = 'y', 'z' # 与上面等价
slot = ['y', 'z']
slot = 'y' # 也是一个元组
def init(self):
self.y = 5
self.z = 10
def show(self):
print(self.x, self.y)
a = A()
a.show()
print('A:', A.dict)
print('a:', a.dict) 没有这个属性了
slots不影响继承
# 继承
class A:
x = 1
slots = ('y', 'z') # 一般用元组
slot = 'y', 'z' # 与上面等价
slot = ['y', 'z']
slot = 'y' # 也是一个元组
def init(self):
self.y = 5
self.z = 10
def show(self):
print(self.x, self.y)
class B(A):
pass
print('********')
b = B()
print('B:', b.dict) # {}
内建函数tracemalloc
tracemalloc -Trace memory allocations
# 内建模块,具体信息参考官方文档
import tracemalloc
tracemalloc.start() # 开始跟踪内存分配
d = [dict(zip('xy', (5, 6))) for i in range(1000000)] # 237M
t = [tuple(zip('xy', (5, 6))) for i in range(1000000)] # 191M
snapshot = tracemalloc.take_snapshot() # 快照,当前内存分配
top_stats = snapshot.statistics('lineno') # 快照对象统计,按行统计内存
top_stats = snapshot.statistics('filename') # 参看当前文件总内存
for stat in top_stats:
print(stat)
运算符重载中的反向方法
add 与iadd
class A:
def init(self, x):
self.x = x
def add(self, other):
print('add')
return self.x + other.x
def iadd(self, other):
print('iadd')
return A(self.x + other.x)
def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
raise NotImplementedError
def str(self):
return str(self.x)
repr = str
print('***********')
a = A(4)
c = A(5)
print(a + c) # add 9
print(c + a) # add 9
a += c # iadd 9
print(a)
c += a # iadd 9
print(c)
调用iadd的情况,B类中没有实现add方法,A类中实现了
radd方法,想计算b + a
# 一种实现
class A:
def init(self, x):
self.x = x
def add(self, other):
print('add')
return self.x + other.x
def iadd(self, other):
print('iadd')
return A(self.x + other.x)
def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
raise NotImplementedError
def str(self):
return str(self.x)
repr = str
class B:
def init(self, x):
self.x = x
def add(self, other):
if isinstance(other, type(self)):
return self.x + other.x
else:
return NotImplemented
a = A(2)
bb = B(3)
print('*************')
print(bb + a) # bb.add(a) 找到NotImplemented, 所以反转计算a + bb,近而调用a.radd(bb)
print('+++++++++++')
bb += a # bb没有iadd 找到add, 抛出NotImplemented,所以找到a.radd
print(bb)
另一种实现
class A:
def init(self, x):
self.x = x
def add(self, other):
print('add')
return self.x + other.x
def iadd(self, other):
print('iadd')
return A(self.x + other.x)
def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
raise NotImplementedError
def str(self):
return str(self.x)
repr = str
class B: # 默认未实现add
def init(self, x):
self.x = x
a = A(2)
bb = B(3)
print(bb + a)
print('+++++++++++')
bb += a
print(bb)
实现 1 + a
# 1 + a 方法1
class A:
def init(self, x):
self.x = x
def add(self, other):
print('add')
return self.x + other.x
def iadd(self, other):
print('iadd')
return self.x + other.x
def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
elif isinstance(other, int):
return self.x + other
else:
raise NotImplemented
def str(self):
return str(self.x)
repr = str
a1 = A(3)
print(1 + a1) # 1.add(a), 但是这个加法的返回值是NotImplemented,
解释器发现是这个值,就发生反转相当于计算a1 + 1,所以就从a对象找radd
方法2
# 如果不是整型,返回自己, 1+a 另一种实现
class A:
def init(self, x):
self.x = x
def add(self, other):
print('add')
return self.x + other.x
def iadd(self, other):
print('iadd')
return self.x + other.x
def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
try:
other = int(other)
except ValueError:
other = 0
return self.x + other
def str(self):
return str(self.x)
repr = str
a1 = A(8)
print(1 + a1)
print('abc' + a1)
python中的对象模型
python中任何对象都有类型,可以使用type() 、class查看
继承所有类都继承自object,type类也继承自object,object的父类是空
python中所有类的类型都是type(元类),实例的类型是生成实例的类。
无参构造与值
print(None)
print(Exception) # 无参构造是一个类
print(NotImplemented)