Day16++ 高阶函数
定义:一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),满足其一则为高阶函数。
----迭代器
用于记住遍历的位置的对象;
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
具体用法:
list=[1,2,3,4]
it=iter(list)
for x in it:
print(x)
>>output:1 2 3 4
用迭代器实现自定义sort函数:
def sort(iterable):
"""自定义排序函数"""
ret=[]
for x in iterable:
for i,y in enumerate(ret):
if x>y:
ret.insert(i,x) #insert() 函数用于将指定对象插入列表的指定索引位置。
break
else: #for else循环使用else语句,遇到break就不执行else
ret.append(x) #x比前面所有数字都小,往后添加
return ret
list=[1,5,3,2,6,8]
print(sort(list))
#>>[8, 6, 5, 3, 2, 1]
变形,嵌套函数作为参数:
def comp(a,b,reverse): #函数返回一个布尔值
flag=a>b if reverse else a<b
return flag
def sort(iterable,key=comp,reverse=True):
"""自定义排序函数"""
ret=[]
for x in iterable:
for i,y in enumerate(ret):
if comp(x,y,reverse): #函数作为参数传递
ret.insert(i,x) #insert() 函数用于将指定对象插入列表的指定索引位置。
break
else: #for else循环使用else语句,遇到break就不执行else
ret.append(x) #x比前面所有数字都小,往后添加
return ret
list=[1,5,3,2,6,8]
print(sort(list))
#>>[8, 6, 5, 3, 2, 1]
更简洁的写法:
def sort(iterable,key=lambda a,b:a<b,reverse=False):
"""自定义排序函数"""
ret=[]
for x in iterable:
for i,y in enumerate(ret):
if key(x,y): #函数作为参数传递
ret.insert(i,x) #insert() 函数用于将指定对象插入列表的指定索引位置。
break
else: #for else循环使用else语句,遇到break就不执行else
ret.append(x) #x比前面所有数字都小,往后添加
return ret
list=[1,5,3,2,6,8]
print(sort(list))
filter(function,iterable)
过滤可迭代对象的元素,返回一个迭代器
function一个具有一个参数的函数,返回bool
例如,过滤出数列中能被3整除的数字
list(filter(lambda x:x%3==0,[1,9,55,150,-3,78,28,123]))
map(function,*iterables) ---->map object (映射)
对多个可迭代对象的元素按照指定的函数进行映射时,返回一个迭代器。
list(map(lambda x:2*x+1,range(5)))
dict(map(lambda x:(x%5,x),range(500)))
Currying 柯里化
将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。
z=f(x,y)转换成 z=f(x)f(y)的形式;
def add(x,y):
return x+y
转化为一个参数
def add(x):
def _add(y):
#这里把第二个函数封装到函数里了,按照add里的顺序,先把4传入再返回_add 此时把5传入_add,再4+5
return x+y
return _add
func=add(4)
print(func(5))
闭包
如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包。闭包是Python所支持的一种特性,它让在非global scope定义的函数可以引用其外围空间中的变量,这些外围空间中被引用的变量叫做这个函数的环境变量。环境变量和这个非全局函数一起构成了闭包。
def counter(base):
def inc(step=1):
base=base+step
return base
return inc #返回内部函数时不加括号,只返回函数地址
print(counter(0))
装饰器
定义:装饰python的工具,并通过某种方式增强函数的功能。
def add(x,y):
return x+y
这是一个简单的函数,想要增强它的功能,能够输出被调用过以及调用的参数信息。
def add(x,y):
print('call add,x+y')
return x+y
def logger(fn):
print('begin') #增强的输出
x=fn(4,5)
print('end') #增强的功能
return x
print(logger(add))
装饰器语法:
def logger(fn):
def wrapper(*args,**kwargs): #包装函数
print('begin')
x=fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger #等价于add=logger(add)
#装饰器把add作为参数传递给logger,覆盖add指向logger内存函数,fn保留了add作为自由变量进行原来add的功能
def add(x,y): #业务函数
return x+y
print(add(40,50))
#output:
#begin
#end
#90
文档字符串
要把字符串放在函数里面第一行
def logger(fn):
def wrapper(*args,**kwargs):
"""This is a wrapper"""
print('begin')
x=fn(*args,**kwargs)
print('end')
return x
return wrapper
@logger #等价于add=logger(add)
#装饰器把add作为参数传递给logger,覆盖add指向logger内存函数,fn保留了add作为自由变量进行原来add的功能
def add(x,y):
"""This is a function"""
return x+y
# print(add(40,50))
print('doc=',add.__doc__) #输出文档信息
#output:doc= This is a wrapper
此时输出的文档字符串是wrapper里的,因为装饰器把add指向了包装函数。如果想要输出业务函数里面的文档,通过增加另一个copy函数来实现。因为装饰器,原函数对象的属性都被替换了,我们的需求是查看被封装函数的属性,如何解决?
def copy_properties(src,dst): #属性拷贝
dst.__name__=src.__name__
dst.__doc__=src.__doc__
def logger(fn):
def wrapper(*args,**kwargs):
"""This is a wrapper"""
print('begin')
x=fn(*args,**kwargs)
print('end')
return x
copy_properties(fn,wrapper)
#在返回包装函数的值以前要实现copy,此时add已经被wrapper覆盖掉。原来的add通过fn保存,所以要用fn来copy给wrapper
return wrapper
@logger #等价于add=logger(add)
#装饰器把add作为参数传递给logger,覆盖add指向logger内存函数,fn保留了add作为自由变量进行原来add的功能
def add(x,y):
"""This is a function"""
return x+y
# print(add(40,50))
print('name={},doc={}'.format(add.__name__,add.__doc__))
#output:name=add,doc=This is a function
但是如果在嵌套函数内部用copy把包装函数属性全部覆盖,那么我想要得到包装函数的属性时,会很困难。
带参装饰器
通过copy_properties函数将被包装函数的属性覆盖掉包装函数,用add覆盖wrapper;
# copy增强属性,而不是全体拷贝
def copy_properties(src):
def _copy(dst):
dst.__name__=src.__name__
dst.__doc__=src.__doc__
return dst
return _copy
def logger(fn):
@copy_properties(fn) #wrapper=_copy(wrapper)
#引入带参装饰器,logger的fn是add,add再通过copy函数的src传入
def wrapper(*args,**kwargs):
"""This is a wrapper"""
print('begin')
x=fn(*args,**kwargs)
print('end')
return x
# copy_properties(fn,wrapper)
return wrapper
@logger #等价于add=logger(add)
#装饰器把add作为参数传递给logger,覆盖add指向logger内存函数,fn保留了add作为自由变量进行原来add的功能
def add(x,y):
"""This is a function"""
return x+y
# print(add(40,50))
print('name={},doc={}'.format(add.__name__,add.__doc__))
#output:name=add,doc=This is a function
带参装饰器常用于传递参数给函数内部的闭包使用,使用@functionname(参数列表)方式调用。
functools模块
update.wrapper(wrapper,wrapped,assigned=WRAPPER,ASSIGNMENTS,updated=WRAPPER_UPDATES)
功能与copy类似,wrapper会被wrapped覆盖
def copy_properties(src):
def _copy(dst):
dst.__name__=src.__name__
dst.__doc__=src.__doc__
return dst
return _copy
import functools #找到原来的函数,与copy装饰器功能类似
def logger(fn):
def wrapper(*args,**kwargs):
"""This is a wrapper"""
print('begin')
x=fn(*args,**kwargs)
print('end')
return x
functools.update_wrapper(wrapper,fn)
print('{} {}'.format(id(wrapper),id(fn)))
return wrapper
@logger #等价于add=logger(add)
#装饰器把add作为参数传递给logger,覆盖add指向logger内存函数,fn保留了add作为自由变量进行原来add的功能
def add(x,y):
"""This is a function"""
return x+y
# print(add(40,50))
print('name={},doc={}'.format(add.__name__,add.__doc__))
print(id(add.__wrapped__))
#140386262148864 140386262149296
# name=add,doc=This is a function
# 140386262149296
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
少了个被覆盖的wrapper,所以要用带参传进去。
def copy_properties(src):
def _copy(dst):
dst.__name__=src.__name__
dst.__doc__=src.__doc__
return dst
return _copy
import functools #找到原来的函数,与copy装饰器功能类似
def logger(fn):
@functools.wraps(fn) #少了个被覆盖的,但是wrapper用嵌套函数传进去
def wrapper(*args,**kwargs):
"""This is a wrapper"""
print('begin')
x=fn(*args,**kwargs)
print('end')
return x
# functools.update_wrapper(wrapper,fn)
print('{} {}'.format(id(wrapper),id(fn)))
return wrapper
@logger #等价于add=logger(add)
#装饰器把add作为参数传递给logger,覆盖add指向logger内存函数,fn保留了add作为自由变量进行原来add的功能
def add(x,y):
"""This is a function"""
return x+y
# print(add(40,50))
print('name={},doc={}'.format(add.__name__,add.__doc__))
print(id(add.__wrapped__))
扁平化字典用迭代
"""遍历字典,用items返回字典的键与值"""
for k,v in myArray.items():
print(k,v)
对嵌套的字典调整读取顺序
source={'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}}
#目标字典:{'a.c':2,'d.e':3,'d.f.g':4,'a.b':1}
target={}
#recursion
def flatmap(src,prefix=''):
for key,value in src.items():
if isinstance(value,(list,tuple,set,dict)):
flatmap(value,prefix=prefix+key+'.')
#键后面如果不是单个数值,就要递归,并且加点
else:
target[prefix+key]=value
#print(target)
flatmap(source)
print(target)
#output:{'a.b': 1, 'a.c': 2, 'd.e': 3, 'd.f.g': 4}
存放新顺序的空字典如何不暴露给外界内部的字典?
能否函数就提供一个参数源字典,返回一个新的扁平化字典呢?
递归的时候要把目标字典的引用传递多层,怎么处理?
source={'a':{'b':1,'c':2},'d':{'e':3,'f':{'g':4}}}
#目标字典:{'a.c':2,'d.e':3,'d.f.g':4,'a.b':1}
#recursion
def flatmap(src,dest=None,prefix=''):
"""创建一个扁平化字典,并返回一个新的数据结构"""
if dest==None:
dest={}
for key,value in src.items():
if isinstance(value,(list,tuple,set,dict)):
flatmap(value,dest,prefix=prefix+key+'.')
#键后面如果不是单个数值,就要递归,并且加点
else:
dest[prefix+key]=value
return dest
print(flatmap(source))
面向对象
举例:人有属性和行为;对象就是指把属性与行为归结到某一个对象上
python是OOP语言(面向对象编程)
----属性
class Money: #Money既是类名又是变量
pass
print(Money.__name__)
a=Money
print(a.__name__)
Money=666
print(Money)
obj=Money()
#obj找到根据money类产生的对象,根据对象里面的class找到对应的类
print(obj)
#Money
#Money
#666
#<__main__.Money object at 0x7f8e8ca63f70>
#1。定义一个类
class Person:
pass
#2。根据类,创建一个对象
obj=Person()
#3.给obj增加一些属性 就是对象属性
obj.age=18
obj.name=['yoyo','zaza']
obj.name.append('gigi') #字符型才能访问并增加,数值型不可以
del obj.name
print(obj.__dict__) #查看对象所有属性
#<<output:{'age': 18}
#4。类属性
Person.count=1
print(Person.count)
#<<output:1
print(obj.count) #对象查找属性,优先对象属性,再用类属性
#<<output:1
——限制对象添加的属性:
class Person:
__slots__ = ['age']
#后续对象只能添加age属性,其他属性会报错
pass
p=Person()
p.age=10
print(p.age)
----方法(可以自动补全第一个参数)
分为实例方法,类方法,静态方法
三者区别:接收的第一个参数类型不同
class Person:
#1。实例方法
def eat(self): #self自动填充的实例
print(1 ,self)
#2.类方法
@classmethod
def classmethod(cls): #要求接收的是一个类
print(2,cls)
#3。静态方法
@staticmethod
def staticmethod():#无要求
print(3)
p=Person()
print(p)
p.eat()
# output:
# < __main__.Person object at 0x7fcc37c6a580 >
# 1 <__main__.Person object at 0x7fcc37c6a580>
Person.classmethod()
# output:2 <class '__main__.Person'>
Person.staticmethod()
# output:3
p=Person()
print(p.__dict__)
#output:{}
#方法并不存储在实例对象里面
print(Person.__dict__) #方法只存在类对象里
实例方法:self不需要手动传,解释器会自动把调用对象本身传递过来;但是如果实例方法没有接收任何参数,则会报错(一个自动传,一个不接收)。通过实例可以调用实例方法。
类方法:调用的是类方法
静态方法:用类对象或者实例对象调用都可以
元类:创建类对象的类。作用:创建int类,str类等或者自定义的class类,
用’__ class __'可以往上找
num=10
print(num.__class__)
#<<output:<class 'int'>
class Person:
pass
p=Person()
print(p.__class__)
#<<output:<class '__main__.Person'>
实例对象都有各自的类,数值属于int类,字符串属于string类,那么自定义的实例对象也有其自己的自定义类
num=10
print(num.__class__.__class__) #类对象所对应的类
print(int.__class__)
结果为
<class 'type'>
<class 'type'>
属性相关补充:
1.私有化属性:让访问属性变小,不是所有属性都可以进行修改,保存一些固有属性。类属性(方法)和实例属性(方法)都可以遵循相同的规则。
公有属性:可以实现类内部访问,子类内部访问,模块内其他位置访问,跨模块访问
_x :属性加一个下划线,就是受保护属性。在类内部和延伸类内部都能访问,但是在模块内部可能会受警告;跨模块import,也会收到警告。
__x:属性加两个下划线,只有类内部才能访问,在本模块其他区域都不能访问;import跨模块访问可以。
私有化属性应用:数据保护 数据过滤
class Person:
def __init__(self):
#当我们创建好一个实例对象之后,会自动的调用这个方法来初始化这个对象
self.age=18 #之后的实例对象都有age
pass
p1=Person()
p1.age=19
p2=Person()
p3=Person()
print(p1.age)
print(p2.age)
print(p3.age)
#output:
#>>19
#>>18
#>>18
当添加下划线变成私有属性后,无法修改实例对象并访问
class Person:
def __init__(self):
#当我们创建好一个实例对象之后,会自动的调用这个方法来初始化这个对象
self.__age=18 #私有属性
pass
p1=Person()
p1.__age=19
print(p1.age)
只读属性:私有化属性不能读与写,只读可以选择性公开
想要访问私有属性的话,需要增加类方法,通过return访问
class Person:
def __init__(self):
#当我们创建好一个实例对象之后,会自动的调用这个方法来初始化这个对象
self.__age=18 #私有属性
def getAge(self):
return self.__age
pass
p1=Person()
print(p1.getAge())
#output:>>18
只读属性1:
设置成私有属性,再用@property完成只读
class Person:
def __init__(self):
#当我们创建好一个实例对象之后,会自动的调用这个方法来初始化这个对象
self.__age=18 #私有属性
@property
#可以以使用属性的方式来使用这个方法
def getAge(self):
print('--------get--------')
return self.__age
@getAge.setter
#调用只读属性后对只读属性进行修改,这个装饰器公式背下来
def age(self,value):
print('--------set--------')
self.__age=value
p=Person()
print(p.getAge) #读取函数不要小括号
#output:
#>> --------get--------
##>> 18
p.age=20 #修改只读属性的值
print(p.age)
#>> --------get--------
#>> --------set--------
#>>output:20
另一种修改私有属性的方法
class Person:
def __init__(self):
#当我们创建好一个实例对象之后,会自动的调用这个方法来初始化这个对象
self.__age=18 #私有属性
#可以以使用属性的方式来使用这个方法
def getAge(self):
print('--------get--------')
return self.__age
def setAge(self,value):
print('--------set--------')
self.__age=value
age=property(getAge,setAge)
p = Person()
print(p.age)
#output:
# >> --------get--------
# >> 18
p.age=20
print(p.age)
#>> --------set--------
#>> --------get--------
#>>output:20
print(p.__dict__)
只读属性2:
class Person:
pass
p=Person()
p.age=18
print(p.age)
print(p.__dict__)
#output:
# >>18
# >>{'age': 18}
class Person:
#设置属性:当我们通过实例.属性=值,给一个实例增加一个属性或者修改属性值的时候,都会调用这个方法
#在这个方法内部,才会真正的把这个属性以及对应的数据存储到__dict__字典里面
def __setattr__(self, key, value):
print(key,value)
pass
p=Person()
p.age=18
# print(p.age)
print(p.__dict__)
#output:
# >>age 18
# >>{}
没有给实例增加age属性:通过修改字典的方式
class Person:
#设置属性:当我们通过实例.属性=值,给一个实例增加一个属性或者修改属性值的时候,都会调用这个方法
#在这个方法内部,才会真正的把这个属性以及对应的数据存储到__dict__字典里面
def __setattr__(self, key, value):
if key=='age'and key in self.__dict__.keys():
#把age设置成只读属性,不把它添加到字典里;第二个条件是只读属性已经存在里面了
print('这个属性是只读属性,不能设置数据')
else:
# self.key=value#死循环
self.__dict__[key]=value
pass
p=Person()
p.age=18
print(p.age)
print(p.__dict__)
#output:
# >>18
# >>{'age': 18}
p.age=20
print(p.age)
#output:
# >>这个属性是只读属性,不能设置数据
# >>18
----内置方法
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
p1=Person('ab',18)
print(p1.name)
print(p1.age)
#output:
# >>ab
# >>18
p2=Person('cd',20)
print(p2.name)
print(p2.age)
#output:
# >>cd
# >>20
上面每次p1 p2操作时都要把属性输出一遍,用什么方法可以使p1 p2直接输出?
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self): #必须要返回字符串类型
return '这个人叫%s,现在%s岁了'%(self.name,self.age)
p1=Person('ab',18)
# print(p1.name)
# print(p1.age)
print(p1)
#output:
# >>这个人叫ab,现在18岁了
__call__方法
class Person:
def __call__(self, *args, **kwargs):
print('xxx',args,kwargs)
pass
p=Person()
p(123,name='sz') #直接拿对象去调用,调用的时候可以传参。
#output:xxx (123,) {'name': 'sz'}
创建很多个画笔,画笔的类型(钢笔水笔),画笔的颜色(红黄蓝绿)
怎么传入最简便?
引入 functools模块中的partial
作用:固定传入参数的值
def createPen(p_colour,p_type):
print('%s:它的颜色是%s'%(p_type,p_colour))
createPen('红色','钢笔')
createPen('黄色','钢笔')
createPen('蓝色','钢笔')
createPen('绿色','钢笔')
from functools import partial
pen=partial(createPen,p_type='水笔') #都是水笔,一起传入
pen('红色') #传入第一个参数
pen('黄色')
pen('蓝色')
pen('绿色')
另外的做法:
class PenFactory:
def __init__(self,p_type): #新增通用属性
self.p_type=p_type
def __call__(self, p_colour):
print('%s:它的颜色是%s' % (self.p_type, p_colour))
gangbi=PenFactory('钢笔')
gangbi('红色')
gangbi('黄色')
gangbi('绿色')
——索引操作
class Person:
def __setitem__(self,key,value):#传入索引和值
print("setitem",key,value)
def __getitem__(self,item): #打印会用到这个方法,只需要索引
print("getitem",item)
def __delitem__(self, key):
print("delitem",key)
p=Person()
p["name"]='sz'
#>>setitem name sz
print(p["name"])
#>>getitem name
#>>None
del p["name"]
#>>delitem name
——切片操作
class Person:
def __init__(self):
self.items=[1,2,3,4,5,6,7,8]
def __setitem__(self, key, value): # 传入切片实例和值
print("setitem",key,value)
self.items[key]=value
def __getitem__(self, item): # 打印会用到这个方法,只需要索引
print("getitem",item)
def __delitem__(self, key):
print("delitem",key)
p=Person()
p[0:4:2]=["a","b"] #重新赋值
#>>setitem slice(0, 4, 2) ['a', 'b']
print(p.items)
#>>['a', 2, 'b', 4, 5, 6, 7, 8]
——比较操作
class Person:
def __init__(self,age,height):
self.age=age
self.height=height
#比较操作
def __eq__(self, other): #比较两个相等的实例对象属性
print(self,other)
return self.height==other.height
pass
def __ne__(self, other): #比较两个不相等的实例对象属性
print(other)
return self.age!=other.age
pass
p1=Person(18,180)
p2=Person(17,180)
print(p1==p2)
#>>True
print(p1!=p2)
#>>True
比较方法有:
eq 等于
ne 不相等
lt 小于
le 小于或等于
gt 大于
ge 大于或等于
用装饰器@functools.total_ordering 来补充比较操作,可以实现大于或等于用大于操作与等于操作执行
—— 遍历操作
class Person:
def __init__(self):
self.age=1
def __iter__(self):
return self
def __next__(self):
if self.age>=6:
raise StopIteration('停止迭代')
self.age += 1
return self.age
p=Person()
for i in p:
print(i)
class Person:
def __init__(self):
self.age=1
def __iter__(self):
return self
# def __next__(self):
# self.age += 1
# if self.age>=6:
# raise StopIteration('停止迭代')
# return self.age
def __call__(self, *args, **kwargs):
self.age += 1
if self.age>=6:
raise StopIteration('停止迭代')
return self.age
p=Person()
for i in p:
print(i)
#>>
# 2
# 3
# 4
# 5
#增加条件,迭代到4的时候停止
pt=iter(p.__next__,4)
for i in pt:
print(i)
#>>
# 2
# 3
pr=iter(p, 4)
for i in pr:
print(i)
#>>
# 2
# 3
描述器:调用似有属性进行增删改的时候,是直接调用方法;如果想要通过调 用属性的方法去间接调用这些增改方法,可以用property。
调用方法再打印才能得到结果
如果用property,可以直接用调用属性打印的方法接到结果;或者加上装饰器。
且描述器只在新式类里才有效。
描述器需要使用新式类方法,因为新式类自己继承object,这样才能调用get方法
实现方式二:
#描述器
class Age:
def __get__(self, instance, owner):#系统自己写的方法
print("get")
def __set__(self,instance, owner):
print("set")
def __delete__(self, instance):
print("delete")
class Person:
age=Age() #通过类创建一个实例
def __getattribute__(self, item): #自己写,覆盖掉系统写的方法
print('newget')
p=Person()
p.age=10
print(p.age)
del p.age
#output:
# set
# newget
# None
# delete
资料描述器 get set
非资料描述器 仅仅实现了get方法
资料描述器>实例属性>非资料描述器
#描述器
class Age:
def __get__(self, instance, owner): #系统自己写的方法
print("get")
return instance.v
def __set__(self,instance, value):
print("set",self,instance,value)
instance.v=value
def __delete__(self, instance):
print("delete")
class Person:
age=Age()
#age对象共享,不同的person实例需要不同的对象才能存储数据,所以要用到instance
p1=Person()
p1.age=10
print(p1.age)
p2=Person()
p2.age=11
print(p2.age)
print(p1.age) #还是10
——对象的生命周期
对象被调用后 在自动执行__del__ 释放对象存储空间
——循环引用
类之间相互调用,但是删掉了指向类的变量,导致类无法释放占用空间
内存管理机制=引用计数器机制+垃圾回收机制
计数机制
出现了循环引用
import objgraph
#用objgraph.count()可以查看垃圾回收器跟踪的对象个数
class Person:
pass
class Dog:
pass
p=Person()
print(objgraph.count("Person"))
d=Dog()
print(objgraph.count("Dog"))
p.pet=d
d.master=p
#删除p,d之后,对应的对象是否被释放掉?
del p
print(objgraph.count("Person"))
del d
print(objgraph.count("Dog"))
垃圾回收机制
0级检测了设定的多少次数后,进入1级再到达检测次数后升入2级。
import gc
gc.disable()
print(gc.isenabled())
#>>False
gc.enable()
print(gc.isenabled())
#>>True
print(gc.get_threshold())
#>>(700, 10, 10) 新增的个数减去消亡的个数达到700阈值
通过弱引用释放内存
#类之间相互调用,但是删掉了指向类的变量,导致类无法释'放占用空间
import objgraph
import gc
import weakref
#用objgraph.count()可以查看垃圾回收器跟踪的对象个数
#1.定义两个类
class Person:
pass
class Dog:
pass
#2.根据这两个类,创建出两个实例对象
p=Person()
# print(objgraph.count("Person"))
d=Dog()
# print(objgraph.count("Dog"))
#3.让两个实例对象相互引用,形成循环引用
p.pet=d
# d.master=p
d.master=weakref.ref(p) #弱引用
#4.删除可到达引用之后,测试真实对象是否有被回收
del p
del d
#5.通过"引用技术机制"无法回收,需要借助"垃圾回收机制"进行回收
# gc.collect()
print(objgraph.count("Person"))
#>> 0
print(objgraph.count("Dog"))
#>> 0
小结案例展示
#计算器,实现一些基本的操作,加减乘除运算以及打印结果操作
class Caculator:
__result=0 #不想在外界变动,变成私密属性
@classmethod
def first_value(cls,v): #操作result这个类属性,用类方法
global __result
cls.__result=v
@classmethod
def jia(cls,n):
global __result
cls.__result+=n
@classmethod
def jian(cls,n):
global __result
cls.__result-=n
@classmethod
def cheng(cls,n):
global __result
cls.__result*=n
@classmethod
def result(cls):
print("最后的结果是%d"%cls.__result)
Caculator.first_value(5)
Caculator.jia(4)
Caculator.jian(5)
Caculator.cheng(6)
Caculator.result() #私密属性无法读取了,设置一个类方法,专门输出
如果做多次计算,但此时result保存的值会继续使用多次调用的时候,应该用对象属性,更改后结果:
class Caculator:
def __init__(self,num): #不想在外界变动,变成私密属性
self.__result=num
def jia(self,n):
self.__result += n
def jian(self, n):
self.__result -= n
def cheng(self, n):
self.__result *= n
def result(self):
print("最后的结果是%d"%self.__result)
p=Caculator(5) #每次初始值要重新传入,用实例对象传参
p.jia(4)
p.jian(5)
p.cheng(6)
p.result()
#output:
#>>最后的结果是24
a=Caculator(0)
a.jia(6)
a.jian(2)
a.cheng(3)
a.result()
#output:
#>>最后的结果是12
想要增加一些功能,当输入的值不是int类型,输出TypeError
class Caculator:
def judge(func):
def inner(self,n):
if not isinstance(n,int):
raise TypeError("输出的不是整型数据")
return func(self,n)
return inner
@judge
def __init__(self,num): #不想在外界变动,变成私密属性
self.__result=num
@judge
def jia(self,n):
self.__result += n
@judge
def jian(self, n):
self.__result -= n
@judge
def cheng(self, n):
self.__result *= n
def result(self):
print("最后的结果是%d"%self.__result)
p=Caculator(5) #每次初始值要重新传入,用实例对象传参
p.jia(4)
p.jian('ab')
p.cheng(6)
p.result()
#output:
#>>最后的结果是24
a=Caculator(0)
a.jia(6)
a.jian(2)
a.cheng(3)
a.result()
#output:
#>>最后的结果是12