Python学习笔记(十):魔法方法、属性、和迭代器

      • 一、魔法方法
            • 1. 定义
            • 2. 构造方法
            • 3. 基本的序列和映射规则
      • 二、属性
            • 1. property函数
            • 2. 静态方法和类成员方法
            • 3. 装饰器(decorators)
            • 4. 其他
      • 三、迭代器
            • 1. 迭代器规则
            • 2. 从迭代器得到序列
            • 3. 创建生成器

一、魔法方法

1. 定义

       在Python中,有的名称会在前面和后面加上两个下划线,这种拼写表示名字有特殊含义,所以绝不要在自己的程序中使用这种名字。在Python中,由这些名字组成的集合所包含的方法称为魔法方法。他们是可以给类增加”magic”的特殊方法。

2. 构造方法

       构造方法 __init__ 是一个很奇特的名字,它代表着类似于以前例子中使用过的那种名为init的初始化方法,但构造方法和其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法。在Python所有的魔法方法中,__init__ 是使用最多的一个。
       Python中有一个魔法方法叫做 __del__,它在对象就要被垃圾回收之前调用,但发生调用的具体时间是不可知的,所以建议尽力避免使用 __del__ 方法。

class A:
    def __init__(self):
        self.value = 10

f = A()
f.value              # 10

'''
super()
用于调用父类(超类)的一个方法。super函数很智能的,即使类已经继承多个超类,它也只需使用一次super函数(但要确保所有的超类的构造方法都使用了super函数)。
super函数返回了一个super对象,这个对象负责进行方法解析,当对其属性进行访问时,它会查找所有的超类(以及超类的超类),直到找到所需的属性为止(或者引发异常)。
'''
class Bird:
    def __init__(self):
        self.hungry = True
    def eat(self):
        if self.hungry:
            print('Aaaah...')
            self.hungry = False
        else:
            print('No, thanks!')

b = Bird()
b.eat()             # Aaaah...
b.eat()             # No, thanks!

class FlyBird(Bird):
    pass

f = FlyBird()
f.eat()             # Aaaah...
f.eat()             # No, thanks!

class SongBird(Bird):
    def __init__(self):
        self.sound = 'Squawk!'

s = SongBird()
s.eat()             # 异常:'SongBird' object has no attribute 'hungry',因为__init__被覆盖,此时可使用super函数

class SongBird(Bird):
    def __init__(self):
        super().__init__()
        self.sound = 'Squawk'

s = SongBird()
s.eat()             # Aaaah...
s.eat()             # No, thanks!
s.sound             # 'Squawk'
3. 基本的序列和映射规则
'''
__len__(self)
返回集合中所含项目的数量,使用len()时调用。

__getitem__(self, key)
使用索引访问元素时调用。对于一个序列,键应该是一个 0 至 n-1 的整数(如果是负整数,那么要从末尾开始计数),n 是序列的长度;对于映射来说,可以使用任何不可变类型(Number、String、Tuple)的键。

__setitem__(self, key, value)
对索引值 key 赋值 value 时调用。

__delitem__(self, key)
在对一部分对象使用 del 语句时被调用。
'''
class Sequence():
    def __init__(self):
        self.lists = (0, 1, 2)

s = Sequence()
len(s)              # 异常:object of type 'Sequence' has no len()
s[0]                # 异常:'Sequence' object does not support indexing
s[0] = 1            # 异常:'Sequence' object does not support item assignment
del s[0]            # 异常:'Sequence' object doesn't support item deletion

class Sequence():
    def __init__(self, *args):
        self.lists = args[0]
    
    def __len__(self):
        return len(self.lists)
    
    def __getitem__(self, key):
        return self.lists[key]
    
    def __setitem__(self, key, value):
        self.lists[key] = value
        print(value)
    
    def __delitem__(self, key):
        value = self.lists[key]
        del self.lists[key]
        print(value)

s = Sequence([1, 2, 3, 4, 5, 6])
len(s)              # 6
s[0]                # 1
s[0] = 10           # 10
del s[1]            # 2
s.lists             # [10, 3, 4, 5, 6]

'''
子类化列表、字典和字符串
'''
class CounterList(list):
    def __init__(self, *args):
        super().__init__(*args)
        self.counter = 0
    
    def __getitem__(self, key):
        self.counter += 1
        return super().__getitem__(key)

cl = CounterList([1, 2, 3, 4, 5])
cl                  # [1, 2, 3, 4, 5]
cl.reverse()
cl                  # [5, 4, 3, 2, 1]
cl.counter          # 0
cl[0] + cl[1]       # 9
cl.counter          # 2


二、属性

1. property函数
'''
property([fget[, fset[, fdel[, doc]]]])
fget -- 获取属性值的函数,如果只有该参数,产生的属性是只读的
fset -- 设置属性值的函数
fdel -- 删除属性值函数,如果没有设置该参数,删除属性会报异常
doc -- 属性描述信息
返回属性,可以避免使用get和set的获取和设置属性的代码臃肿
'''

class Person():
    def __init__(self):
        self._score = 90
    
    def getScore(self):
        return self._score
    
    def setScore(self, value):
        if (value >= 0 and value <= 100):
            self._score = value
        else:
            print('Wrong Score') 
    
    def delScore(self):
        del self._score
    
    score = property(getScore, setScore, delScore, "I'm the 'score' property.")

p = Person()
p.score             # 90
p.score = 120       # Wrong Score
p.score = 100
p.score             # 100
del p.score
p.score             # 异常:'Person' object has no attribute '_score'
2. 静态方法和类成员方法

       静态方法和类成员方法在创建时分别被装入 staticmethod 类型和 classmethod 类型的对象中。静态方法的定义没有self参数,且能够被类本身直接调用;类方法在定义时需要名为 cls 的类似于 self 的参数,类成员方法可以直接用类的具体对象调用,但 cls 参数是自动被绑定到类的。

class MyClass:
    def smeth():
        print('This is a static method')
    smeth = staticmethod(smeth)
    
    def cmeth(cls):
        print('This is a class method of', cls)
    cmeth = classmethod(cmeth)

MyClass.smeth()     # This is a static method
MyClass.cmeth()     # This is a class method of 
3. 装饰器(decorators)

       在Python2.4中,引入了装饰器,它能对任何可调用的对象进行包装,既能够用于方法也能够用于函数。使用@操作符,在方法或函数的上方将装饰器列出,从而指定一个或者更多的装饰器(多个装饰器在应用时的顺序与指定顺序相反)。

'''
@property
@staticmethod
@classmethod
'''

class Person():
    def __init__(self):
        self._score = 90
    
    @property
    def score(self):
        return self._score
    
    @score.setter
    def score(self, value):
        if (value >= 0 and value <= 100):
            self._score = value
        else:
            print('Wrong Score') 
    
    @property
    def percent(self):
        return self._score/100

p = Person()
p.score             # 90
p.percent           # 0.9
p.score = 100
p.score = 120       # Wrong Score
p.score             # 100
p.percent           # 1.0
p.percent = 0.9     # 异常:can't set attribute

class MyClass:
    @staticmethod
    def smeth():
        print('This is a static method')
    
    @classmethod
    def cmeth(cls):
        print('This is a class method of', cls)

MyClass.smeth()     # This is a static method
MyClass.cmeth()     # This is a class method of 
4. 其他

如果访问 a.x 不存在,那么就要转向到某个操作,我们把这种情况称之为“拦截”(intercept)。 __getattr__、__setattr__和它的朋友们用于拦截对象的所有属性访问。

'''
__dict__:存储属性和方法
__getattr__(self, name):当属性name被访问且对象没有相应的属性时被自动调用
__setattr__(self, name, value):当试图给属性name赋值时自动调用
__delattr__(self, name):当试图删除属性name时被自动调用
__getattribute__(self, name):当属性name被访问时自动被调用

'''

class Attribute():
    def __init__(self):
        self.name = 'name'
    
    def __getattr__(self, name):
        print(name, 'is not in Attribute')
    
    def __setattr__(self, name, value):
        print('setattr success')
        self.__dict__[name] = value
    
    def __delattr__(self, name):
        del self.__dict__[name]

a = Attribute()     # setattr success
a.name              # 'name'
a.attr              # attr is not in Attribute
a.name = 'value'    # setattr success
a.attr = 'attr'     # setattr success
a.name              # 'value'
a.attr              # 'attr'
del a.attr
a.attr              # attr is not in Attribute


三、迭代器

1. 迭代器规则

       迭代的意思是重复做一些事很多次,就像在循环中做的那样。在 for 循环中对序列和字典进行迭代是很好理解的事,但实际上,也能对其他的对象进行迭代:实现 __iter__方法的对象。
       __iter__方法返回一个迭代器(iterator),所谓迭代器就是具有 __next__ 方法(这个方法在调用时不需要任何参数)的对象。在调用 __next__ 方法时,迭代器会返回它的下一个值,如果 __next__ 方法被调用,但迭代器没有值可以返回,就会引发一个 StopIteration 异常。内建函数 next() 可以用于访问 __next__ 方法。任何实现了 __iter__ 和 __next__ 方法的对象都是迭代器。

class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1
    
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a
    
    def __iter__ (self):
        return self

fibs = Fibs()
fibs                # <__main__.Fibs object at 0x101f45048>

for f in fibs:
    if f < 100:
        print(f)    # 输出:1 1 2 3 5 8 13 21 34 55 89
    else:
        break

it = iter([1, 2, 3])
it                  # 
next(it)            # 1
next(it)            # 2
next(it)            # 3
next(it)            # 异常:StopIteration
2. 从迭代器得到序列

       除了在迭代器和可迭代对象上进行迭代外,还能把它们转换为序列。在大部分能使用序列的情况下(除了在索引或者分片等操作中),能使用迭代器或者可迭代对象替换。

class TestIterator:
    value = 0
    def __next__(self):
        self.value += 1
        if self.value > 10: raise StopIteration
        return self.value
    
    def __iter__(self):
        return self

ti = TestIterator()
ti                  # <__main__.TestIterator object at 0x103944198>
list(ti)            # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for t in ti:
    print(t)        # 无输出,生成器只能迭代一次 
3. 创建生成器

       生成器是创建迭代器的一种简单而强大的工具,只是这种迭代器更加优雅,它不需要 __iter__ 和 __next__ 方法,只需要在返回数据的时候使用 yield 语句。每次 __next__ 调用时,生成器再恢复它离开的位置(它记忆语句最后一次执行的位置和所有的数据值)。
       生成器是一个包含 yield 的函数,当它被调用时,在函数体中的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,直到遇到一个 yield 或者 return 语句。yield 语句意味着应该生成一个值。return 语句意味着生成器要停止执行(不再生成任何东西,return 语句只有在一个生成器中使用时才能进行无参数调用)。
       换句话说,生成器是由两部分组成:生成器的函数和生成器的迭代器。生成器的函数是用 def 语句定义的,包含 yield 的部分;生成器的迭代器是这个函数返回的部分。按一种不是很准确的说法,两个实体经常被当做一个,合起来叫做生成器。生成器的函数返回的迭代器可以像其他的迭代器那样使用。

'''
生成器函数
'''
def Fibs():
    a, b = 0, 1
    while (b < 100):
        a, b = b, a + b
        yield a

fibs = Fibs()
fibs                      # 

for f in fibs:
    print(f)              # 输出:1 1 2 3 5 8 13 21 34 55 89

list(fibs)                # []

'''
生成器表达式
'''
squares = (i*i for i in range(5))
squares                    #  at 0x103054570>

list(squares)              # [0, 1, 4, 9, 16]

for s in squares:
    print(s)               # 无输出,生成器只能迭代一次

sum(i*i for i in range(5)) # 30

'''
对生成器使用 send() 方法
'''
def repeater(value):
     while True:
         new = (yield value)
         if new is not None:value = new

r = repeater(10)
next(r)                    # 10
r.send('hello world!')     # 'hello world!'
next(r)                    # 'hello world!'





以上全部内容参考书籍如下:
Magnus Lie Hetland《Python基础教程(第2版)》

你可能感兴趣的:(Python学习笔记)