九.类的进化(魔法方法、特性和迭代器)

关键词:

__init__, __del__, 类的序列化操作,@property,迭代器,生成器,静态方法与类方法,__new__,__call__

0.Python中单下划线_,双下划线__有特殊意义。

1.构造函数和析构函数:

class Test:
    def __init__(self): #构造函数,创造实例默认执行
        pass
    def __del__(self):  #析构函数,程序全部运行完执行
        pass

2.类的序列和映射协议,让类的实例可以实现类似序列的操作。

class Test:
    # 构造函数,创造实例默认执行
    def __init__(self):
        self.date={} #这个方法通常要设置字典为数据类型
        print("Default start")
    # 析构函数,程序全部运行完执行
    def __del__(self):
        print("Default end")
    # 让实例可直接设置:A[key]=...
    def __setitem__(self, key, value):
        self.date[key]=value
    # 让实例可直接访问:A[item]
    def __getitem__(self, item):
        return self.date[item]
    # 让实例可直接运行del A[key]
    def __delitem__(self, key):
        del self.date[key]
    # 让实例可直接运行len(A)
    def __len__(self):
        return len(self.date)
A=Test()
#默认运行实例中的__setitem__
A["k1"]="abc"
A["k2"]="ABC"
#默认运行实例中的__getitem__
print(A["k1"],A["k2"]) #abc ABC
print(A.date) #{'k1': 'abc', 'k2': 'ABC'}
#默认运行实例中的__len__
print(len(A)) #2
#默认运行__delitem__
del A["k1"]
print(A.date) #{'k2': 'ABC'}

3.类中函数property,通过设置property能够实现类中某些函数.

class C:
    def __init__(self):
        self._x = 123
    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")
c=C()
print(c.x) #c.x will invoke the getter
c.x=456 #c.x = value will invoke the setter
del c.x #del c.x the deleter

3.5属性方法@property:

#装饰器写法@property
class C:
    def __init__(self):
        self._x = 456
    @property #属性化当前函数,getx相当于变成了属性,所以访问不能加括号
    def getx(self):
        return self._x
    @getx.setter #getx属性进行设置
    def getx(self, value):
        self._x = value
    @getx.deleter #getx属性进行设置
    def getx(self):
        del self._x
c=C()
print(c.getx) #操作不能加(),因为变成了属性
c.getx=123456789
print(c.getx)
del c.getx
#print(c.getx)

4.迭代器,一层层访问数据,不同于列表要把全部数据加载到内存

it=iter(range(10))    #iter()函数获得一个迭代器
print("first",it.__next__())
print("second",it.__next__())
print("third",it.__next__())
print(next(it))
print(next(it))
class TestIterator:
    def __init__(self):
        self.value=0
    def __next__(self):   #必须要有__iter__方法才可以实现迭代
        self.value+=1
        if self.value>10: raise StopIteration
        return self.value
    def __iter__(self):   #__iter__方法让类成为迭代器,包含__next__方法
        return self          #可以不用设置其他参数
TI=TestIterator()
print(list(TI))

5.生成器,包含yield语句的函数都被称为生成器

  • 生成器不是使用return返回一个值,而是可以生产多个值,每次一个。每次使用yield生成一个值后,函数都将冻结,等待下次调用。
  • 应用如下:
    datelist=[[1,2,3],[4,5],[6]]
    def generator(date):
        for i in date:
            for j in i:
                yield j 
    #generator(datelist)生成器是以迭代的形式推进数据,而不是数据全部处理好一个一个调用。大数据时能省内存
    for ii in generator(datelist):
        print(ii, generator(datelist))

 6.静态方法、类方法:@staticmethod和@classmethod的作用于区别

  • method或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。这有利于组织代码。
  • @staticmethod和@classmethod都可以直接类名.方法名()来调用,区别:
    • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
    • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
    • 如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
    • 而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码
class A(object):
    bar = 1
    def foo(self):
        print('foo')
    @staticmethod
    # 这里的self不是实例中的self,仅仅是个参数,如果没有@staticmethod,self表示实例
    def static_foo(self):
        print('static_foo')
        print(A.bar)
    @classmethod
    # 这里必须要有cls,表示当前的类
    def class_foo(cls):
        print('class_foo')
        print(cls.bar)
        A().foo()
A.static_foo(123)  #static_foo , 1
A.class_foo()  #class_foo, 1, foo
class Dog(object):
    def __init__(self,name):
        self.name = name
    @staticmethod #实际上跟类没什么关系了
    def eat(self):
        print("%s is eating %s" %(self.name,'dd'))
d = Dog("ChenRonghua")
d.eat(d) #实例下调用,要把实例本身d传进去,但是这样影响直接调用

class Dog(object):
    name = "huazai"
    def __init__(self,name):
        self.name = name
    @classmethod
    def eat(cls,self):
        print("%s is eating %s" %(cls.name,'dd'),self.name)
    def talk(self):
        print("%s is talking"% self.name)
#Dog.eat()
d=Dog("ChenRongHua")
d.eat(d) #类方法也可以传递实例变量,但是这样影响直接调用

 7.__new__,__init__,__call__方法

# __new__() 表示类实例化过程中,是否要使用该类下 __init__() 方法,__new__() 在 __init__() 之前执行
# __new__()是针对当前类的,与__init__()相同,不是针对继承类和父类的
# __call__()是实例化后,实例加()执行
# 执行顺序__new__() --> __init__() --> __call__()
class Stranger(object):
    pass
class Foo(object):
    def __init__(self, *args, **kwargs):
        print("Foo:__init__")
    #new在init之前,所以并没有实例化的参数self
    def __new__(cls, *args, **kwargs):
        print("Foo:__new__")
        # return object.__new__(Stranger) #如果是这句,指向不是本类,而是其他类,则不执行__init__
        return object.__new__(cls) #这句不写也是不执行__init__
foo=Foo() #Foo:__new__ , Foo:__init__

class Goo(Foo):
    def __init__(self, *args, **kwargs):
        super().__init__()
        print("Goo:__init__")
    #子类不写new,会自动调用父类的new
    def __new__(cls, *args, **kwargs):
        print("Goo:__new__")
        return object.__new__(cls) #通常写法,写此句显然不会执行父类的new
        #return Foo.__new__(cls) #通过父类的new传递了当前cls,写此句显然会执行父类的new
    #实例后加()执行
    def __call__(self, *args, **kwargs):
        print("Goo:__call__")
goo=Goo() #Goo:__new__, Foo:__init__, Goo:__init__
goo() #Goo:__call__,相当于goo.__call__(),Goo()()

 

posted @ 2019-03-30 11:48  观井映天  阅读(231)  评论(0编辑  收藏  举报