Python-100-Days之 语言进阶高阶函数 Day16++

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值