作者: qianlong21st | 来源:发表于2018-05-19 12:08 被阅读0次

1 构造函数

1.1 构造函数及析构函数的定义及使用

  • 构造函数的函数名为:init
  • 析构函数的函数名为:del
class Test:
    #python中构造函数的名称为__init__
    def __init__(self,value=64):
        self.val=value

    def printVal(self):
        print(self.val)

    #python中析构函数名称为__del__
    def __del__(self):
        print("deconstructor.....")

t=Test()
t.printVal()

#带参数的构造函数
t2=Test("Hello World!")
t2.printVal()

1.2 构造函数的重载

  • 重写构造函数时,必须调用超类(继承的类)的构造函数,否则可能无法正确地初始化对象。请看下面的Bird类:
class Bird:
    def __init__(self):
        self.hungry=True
    def eat(self):
        if self.hungry:
            print('Aaaah....')
            self.hungry=False
        else:
            print('No,thanks.')

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

bird=Bird()
bird.eat()
bird.eat()

songBird=SongBird()
songBird.sing()
songBird.eat()
songBird.eat()

# 运行结果如下
# E:\henry\Python\venv\Scripts\python.exe E:/henry/Python/Class2.py
# Traceback (most recent call last):
# Aaaah....
# No,thanks.
# Squawk!
#   File "E:/henry/Python/Class2.py", line 29, in <module>
#     songBird.eat()
#   File "E:/henry/Python/Class2.py", line 5, in eat
#     if self.hungry:
# AttributeError: 'SongBird' object has no attribute 'hungry'
# 
# Process finished with exit code 1

由运行结果可知在子类中不存在属性hungry。因为在SongBird中重写了构造函数,但新的构造函数没有包含任何初始化属性hungry的代码。要消除这种错误, SongBird的构造函数必须调用其超类(Bird)的构造函数,以确保基本的初始化得以执行。为此,有两种方法:调用未关联的超类构造函数,以及使用函数super。

class SongBird(Bird):
    def __init__(self):
        #方案1:调用未关联的超类构造函数
        # Bird.__init__(self)
        #方案2:使用super函数,默认情况下super()==super(SongBird,self)
        #super(SongBird,self).__init__()
        #方案3:使用super函数,super()将返回一个父类对象
        # super().__init__()
        self.sound='Squawk!'
    def sing(self):
        print(self.sound)

2 元素访问

2.1 基本的序列和映射协议

序列和映射基本上是元素(item)的集合,要实现它们的基本行为(协议),不可变对象需要实现2个方法,而可变对象需要实现4个。

  • __len__(self):这个方法应返回集合包含的项数,对序列来说为元素个数,对映射来说为键-值对数。如果len返回零(且没有实现覆盖这种行为的__nonzero__),对象在布尔上下文中将被视为假(就像空的列表、元组、字符串和字典一样)。
  • __getitem__(self, key):这个方法应返回与指定键相关联的值。对序列来说,键应该是0~n-1的整数(也可以是负数,这将在后面说明),其中n为序列的长度。对映射来说,键可以是任何类型。
  • __setitem__(self, key, value):这个方法应以与键相关联的方式存储值,以便以后能够使用__getitem__来获取。当然,仅当对象可变时才需要实现这个方法。
  • __delitem__(self, key):这个方法在对对象的组成部分使用__del__语句时被调用,应删除与key相关联的值。同样,仅当对象可变(且允许其项被删除)时,才需要实现这个方法。
    对于这些方法,还有一些额外的要求。
  • 对于序列,如果键为负整数,应从末尾往前数。换而言之, x[-n]应与x[len(x)-n]等效。
  • 如果键的类型不合适(如对序列使用字符串键),可能引发TypeError异常。
  • 对于序列,如果索引的类型是正确的,但不在允许的范围内,应引发IndexError异常。
    要了解更复杂的接口和使用的抽象基类(Sequence),请参阅有关模块collections的文档。
    下面来试一试,看看能否创建一个无穷序列。
def checkIndex(key):
    if isinstance(key,int)==False:
        raise TypeError
    if key<0:
        raise IndexError

class ArithmeticSequence:
    def __init__(self,start=0,step=1):
        """
        start   序列中的第一个值
        step    两个相邻值的差
        changed 一个字典,包含用户修改后的值
        """
        self.start=start
        self.step=step
        self.changed={}

    def __getitem__(self, key):
        """
        从算术序列中取一个元素
        """
        checkIndex(key)
        try:
            return self.changed[key]
        except KeyError:
            return self.start+key*self.step

    def __setitem__(self, key, value):
        """
        修改算术序列中的元素
        :param key:
        :param value:
        :return:
        """
        checkIndex(key)
        self.changed[key]=value

s=ArithmeticSequence(1,2)
print(s[4])
s[4]=2
print(s[4])
print(s[5])
print(s[-1])

# 执行结果为:
# E:\henry\Python\venv\Scripts\python.exe E:/henry/Python/ArithSequence.py
# 9
# 2
# 11
# Traceback (most recent call last):
#   File "E:/henry/Python/ArithSequence.py", line 43, in <module>
#     print(s[-1])
#   File "E:/henry/Python/ArithSequence.py", line 22, in __getitem__
#     checkIndex(key)
#   File "E:/henry/Python/ArithSequence.py", line 5, in checkIndex
#     raise IndexError
# IndexError

2.2 继承list,dict,str

class CounterList(list):
    def __init__(self, *para):
        self.counter = 0
        super().__init__(*para)

    # 相当于c++中的取值运算符operator[]
    def __getitem__(self, item):
        self.counter += 1
        return super().__getitem__(item)


c = CounterList(range(10))
print(c)
c.reverse()
print(c)
del c[0]
print(c)
print(c[3] + c[4] + c[5])
print(c.counter)

# E:\henry\Python\venv\Scripts\python.exe E:/henry/Python/CounterList.py
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# [8, 7, 6, 5, 4, 3, 2, 1, 0]
# 12
# 3

3 特性

3.1 poperty函数

下述代码中set_size和get_size中都是使用元组来给width和height属性进行赋值及访问的,在这里假想了一个size属性,而若想使得存在真正的size属性,同时有不改变之前代码,可以使用property函数来生成一个真正的size属性。

class Rectangle:
    def __init__(self):
        self.width = 0
        self.height = 0
    def set_size(self, size):
        self.width, self.height = size
    def get_size(self):
        return self.width, self.height
    # 使用property属性,使得size可以向width,height属相那样使用
    size=property(get_size,set_size)


rect=Rectangle()
rect.set_size((640,480))
print(rect.get_size())
rect.width=960
print(rect.get_size())

rect.size=(100,50)
print(rect.get_size())

# 运行结果
# E:\henry\Python\venv\Scripts\python.exe E:/henry/Python/Rectangle.py
# (640, 480)
# (960, 480)
# (100, 50)

3.2 静态方法和类方法

静态方法和类方法是这样创建的:将它们分别包装在staticmethod和classmethod类的对象中。静态方法的定义中没有参数self,可直接通过类来调用。类方法的定义中包含类似于self的参数,通常被命名为cls。对于类方法,也可通过对象直接调用,但参数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)
class MyClass:
    # 静态方法
    @staticmethod
    def smeth():
        print("this is a static method")

    # 类方法
    @classmethod
    def cmeth(cls):
        print('this is a class method of',cls)


MyClass.cmeth()
MyClass.smeth()
c=MyClass()
c.smeth()
c.cmeth()
print(c.__dict__)
print(MyClass.__dict__)

#执行结果为:
# this is a class method of <class '__main__.MyClass'>
# this is a static method
# this is a static method
# this is a class method of <class '__main__.MyClass'>
# {}
# {'__module__': '__main__', 'smeth': <staticmethod object at 0x00000000021DEAC8>, 'cmeth': <classmethod object at 0x00000000021DEBA8>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}

3.3 getattrsetattr等方法

可以拦截对对象属性的所有访问企图,其用途之一是在旧式类中实现特性(在旧式类中,函数property的行为可能不符合预期)。要在属性被访问时执行一段代码,必须使用一些魔法方法。下面的四个魔法方法提供了你需要的所有功能(在旧式类中,只需使用后面三个)。

  • __getattribute__(self, name):在属性被访问时自动调用(只适用于新式类)。
  • __getattr__(self, name):在属性被访问而对象没有这样的属性时自动调用。
  • __setattr__(self, name, value):试图给属性赋值时自动调用。
  • __delattr__(self, name):试图删除属性时自动调用。
    相比函数property,这些魔法方法使用起来要棘手些(从某种程度上说,效率也更低),但它们很有用,因为你可在这些方法中编写处理多个特性的代码。然而,在可能的情况下,还是使用函数property吧。
class Rectangle:
    def __init__(self):
        self.width=0
        self.height=0

    def __setattr__(self, name, value):
        print("__setattr__({},{},{})".format(self,name,value))
        if name=='size':
            self.width,self.height=value
        else:
            self.__dict__[name]=value

    def __getattr__(self, name):
        print('__getattr__({},{})'.format(self,name))
        if name=='size':
            return self.width,self.height
        else:
            raise AttributeError

rect=Rectangle()
#实际上并不存在size这个属性,赋值时对其进行了拦截实际上值是赋给了(rect.width,rect.height),而取出其值时由于不存在size属性,所以会调用__getattr__函数
rect.size=(640,480)
print(rect.size)


# 下述语句中赋值时均会调用__setattr__属性函数,而访问其值时由于对应的属性均已经存在,所以不会调用__getattr__函数
rect.text='hello world!'
print(rect.text)
rect.num=24
print(rect.num)
rect.nums=(34,57)
print(rect.nums)


# E:\henry\Python\venv\Scripts\python.exe E:/henry/Python/Rectangle2.py
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,width,0)
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,height,0)
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,size,(640, 480))
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,width,640)
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,height,480)
# __getattr__(<__main__.Rectangle object at 0x00000000028EEC18>,size)
# (640, 480)
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,text,hello world!)
# hello world!
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,num,24)
# 24
# __setattr__(<__main__.Rectangle object at 0x00000000028EEC18>,nums,(34, 57))
# (34, 57)
# 
# Process finished with exit code 0

4 迭代器

4.1 迭代器创建

迭代(iterate)意味着重复多次,就像循环那样。本书前面只使用for循环迭代过序列和字典,但实际上也可迭代其他对象:实现了方法__iter__的对象。方法 __iter__返回一个迭代器,它是包含方法__next__的对象,而调用这个方法时可不提供任何参数。当你调用方法__next__时,迭代器应返回其下一个值。如果迭代器没有可供返回的值,应引发StopIteration异常。你还可使用内置的便利函数next,在这种情况下, next(it)it.__next__()等效。

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

    def __iter__(self):
        return self

fibs=Fibs()
for f in fibs:
    if f>1000:
        break
    else:
        print(f,end=' ')

# E:\henry\Python\venv\Scripts\python.exe E:/henry/Python/iter.py
# 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 
# Process finished with exit code 0

注意到这个迭代器实现了方法__iter__,而这个方法返回迭代器本身。在很多情况下,都在另一个对象中实现返回迭代器的方法__iter__,并在for循环中使用这个对象。

4.2 使用list将迭代器转为序列

>>> 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()
>>> list(ti)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

5 生成器

5.1 创建生成器

生成器是一种使用普通函数语法定义的迭代器。包含yield语句的函数都被称为生成器。这可不仅仅是名称上的差别,生成器的行为与普通函数截然不同。差别在于,生成器不是使用return返回一个值,而是可以生成多个值,每次一个。每次使用yield生成一个值后,函数都将冻结,即在此停止执行,等待被重新唤醒。被重新唤醒后,函数将从停止的地方开始继续执行。

def flatten(nested):
    for sublist in nested:
        for element in sublist:
            # 包含yield语句的函数叫做生成器,生成器不是使用return返回一个值,而是每次生成一个值。
            # 每次使用yield生成一个值后,函数都将倍冻结,即在此停止执行,等待重新被唤醒。
            # 被唤醒后,函数将从停止的地方开始继续执行。
            yield element

nested=[[1,2],[3,4],[5]]

print(type(flatten(nested)))
#可以将生成器作为list的参数,将其转化为序列
print(list(flatten(nested)))
#可以使用for循环来迭代生成器
for num in flatten(nested):
    print(num,end=' ')

# 执行结果
# E:\henry\Python\venv\Scripts\python.exe E:/henry/Python/Generator
# <class 'generator'>
# [1, 2, 3, 4, 5]
# 1 2 3 4 5 
# Process finished with exit code 0

5.2 生成器推导

生成器推导(也叫生
成器表达式)。其工作原理与列表推导相同,但不是创建一个列表(即不立即执行循环),而
是返回一个生成器,让你能够逐步执行计算。

>>> g = ((i + 2) ** 2 for i in range(2, 27))
#可是使用list来返回一个序列
>>> list(g)
[16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400,
441, 484, 529, 576, 625, 676, 729, 784]
#list执行之后到了生成器的末尾,因此会引发StopIteration异常
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
#使用next之后生成器返回一个值,再次调用list时生成器返回后续的值
>>> g = ((i + 2) ** 2 for i in range(2, 27))
>>> next(g)
16
>>> list(g)
[25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441,
 484, 529, 576, 625, 676, 729, 784]
>>>

如你所见,不同于列表推导,这里使用的是圆括号。在像这样的简单情形下,还不如使用列表推导;但如果要包装可迭代对象(可能生成大量的值),使用列表推导将立即实例化一个列表,从而丧失迭代的优势。另一个好处是,直接在一对既有的圆括号内(如在函数调用中)使用生成器推导时,无需再添加一对圆括号。换而言之,可编写下面这样非常漂亮的代码:sum(i ** 2 for i in range(10))

6 八皇后问题

def conflict(state,nextX):
    nextY=len(state)
    for i in range(nextY):
        if abs(state[i]-nextX) in (0,nextY-i):
            return True
    return False

def queens(num, state=()):
    for pos in range(num):
        if not conflict(state, pos):
            if len(state) == num - 1:
                yield (pos,)    #以元组的形式返回
            else:
                #向递归调用提供的是当前行上面的所有位置
                for result in queens(num, state+(pos,)):  #递归调用返回的是当前行下面的所有皇后的位置
                    yield (pos,)+result #因此应当将当前位置插入到递归调用返回的结果之前


print(list(queens(8)))

相关文章

网友评论

      本文标题:

      本文链接:https://www.haomeiwen.com/subject/hkyodftx.html