快乐学习
前程无忧、中华英才非你莫属!

Day9-人生苦短我学python

双下划线__future__或单下划线有特殊含义,在Python中,这些名字的集合称为魔法方法:最重要的是__init__和一些处理访问对象的方法,这些方法允许你创建自己的序列或者是映射.

<span style="font-family: SimHei; font-size: 19px;"--<----

准备工作:

将__metaclass__=type放在模块的最开始位置,以确保类时最新式的。考虑下面两个类

- class NewStyle(object):
-     more_code_here
- class OldStyle:
-     more_code_here

如果文件以__metclass__=type开始,那么这个类都是新式类

构造方法:

和普通方法的不同在于:当一个对象被创建后,会立即调用构造方法。因此,之前章节的

- >>>f = FooBar()
- >>>f.init()

等同于

- >>>f = FooBar()
- >>> class FooBar:
- def __init__(self):
- self.var = 42

-

-

- >>> f = FooBar()
- >>> f.var
- 42

#如果有默认参数呢?

- >>> class Foobar:
- def __init__(self,value=42):
- self.var = value
-

- >>> f = Foobar()     #无参的话,使用默认值

- >>> f.var
- 42

- >>> f1 = Foobar(44)  #有参的话,使用新参数

- >>> f1.var
- 44

在Python中__init__是使用最多的一个.

python中有个魔法方法__del__,析构方法,她在对象要被垃圾回收之前调用.但是发生调用的具体时间是不可知的。

所以建立尽力避免使用__del__函数.

重写一般方法和特殊构造方法:

- >>> class A:
- ...     def hello(self):
- ...         print 'Hello,World!'

- ...
- >>> class B(A): pass

- ...
- >>> class C(B): pass

- ...

#看看他们的工作机制

#c是C类的实例,当c调用hello(),首先找自己类有没有,没有话,去超类B中找,如果还没有话,

就去超类的超类去找。还没有的话,就报错。

- >>> c = C()
- >>> c.hello()
- Hello,World!

如果重写B类,hello的方法呢?

- >>> class A:
- ...     def hello(self):
- ...         print "Hello,I am A!"

- ...
- >>> class B(A):
- ...     def hello(self):
- ...         print "Hello,I am B!"

- ...
- >>> b = B()
- >>> b.hello()
- Hello,I am B!

重写是继承机制中的一个重要内容,对于构造方法尤其重要。构造方法用来初始化新创建对象的状态,大多数子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。虽然重写的机制对于所有方法来说都是一样的,但是当处理构造方法比重写普通方法时,更可能遇到特别的问题:如果一个类的构造方法被重写,那么就需要调用超类的构造方法,否则对象不会被正确初始化。看下面的例子

- class Bird:
-     def __init__(self):
-         self.hungry = True

-     def eat(self):
-         if self.hungry:
-             print "Ahaha..."

-             self.hungry = False

-         else:
-             print "No, Thanks!"

该类定义鸟的基本功能吃,吃饱了就不再吃

输出结果:

- >>> b = Bird()
- >>> b.eat()
- Ahaha...
- >>> b.eat()
- No, Thanks!
- 下面一个子类SingBird,
- class SingBird(Bird):
-     def __init__(self):
-         self.sound = 'squawk'

-     def sing(self):
-         print self.sound

输出结果:

- >>> s = SingBird()
- >>> s.sing()
- squawk

SingBird是Bird的子类,但如果调用Bird类的eat()方法时,

- >>> s.eat()
- Traceback (most recent call last):
-   File "<pyshell#5>", line 1, in <module>
-     s.eat()
-   File "D:\Learn\Python\Person.py", line 42, in eat
-     if self.hungry:
- AttributeError: SingBird instance has no attribute 'hungry'

代码错误很清晰,SingBird中初始化代码被重写,但没有任何初始化hungry的代码

<span style="font-family: SimHei; font-size: 19px;"--<----

调用未绑定的超类构造方法:

- class SingBird(Bird):
-     def __init__(self):
-         Bird.__init__(self)    #增加这行代码就搞定

-         self.sound = 'squawk'

-     def sing(self):
-         print self.sound
- >>> sb = SingBird()
- >>> sb.sing()
- squawk
- >>> sb.eat()
- Ahaha...
- >>> sb.eat()
- No, Thanks!

<span style="font-family: SimHei; font-size: 19px;"--<----

使用super函数:

- __metaclass__=type
- class Bird:
-     def __init__(self):
-         self.hungry = True

-     def eat(self):
-         if self.hungry:
-             print "Ahaha..."

-             self.hungry = False

-         else:
-             print "No, Thanks!"

- class SingBird(Bird):
-     def __init__(self):
-         super(SingBird,self).__init__()
-         self.sound = 'squawk'

-     def sing(self):
-         print self.sound

Note:

1. __metaclass__=type 必不可少,否则报错如下:

- >>> sb = SingBird()
- Traceback (most recent call last):
-   File "<pyshell#5>", line 1, in <module>
-     sb = SingBird()
-   File "D:\Learn\Python\Person.py", line 51, in __init__
-     super(SingBird,self).__init__()
- TypeError: must be type, not classobj

2. super(SingBird,self).__init__()  多了这么一句

输出结果:

- >>> sb = SingBird()
- >>> sb.sing()
- squawk
- >>> sb.eat()
- Ahaha...
- >>> sb.eat()
- No, Thanks!

 

基本的序列和映射规则:

序列和映射是对象的集合,为了实现他们的基本行为,如果对象是不可变的,那么就需要两个魔法方法,如果对象时可变的,那么就需要四个魔法方法

__len__(self):返回集合中所含项目的数量

__getitem__(self,key):返回与所给的键对应的值

__setitem__(self,key,value):按一定的方法存储和key相关的value

__delitem__(self,key):删除对象相关的键

实践一下,创建一个无穷序列

- def checkIndex(key):
-     if not isinstance(key,(int,long)):
-         raise TypeError
-     if key<0:
-         raise IndexError
- class ArithmeticSequence:
-     def __init__(self,start=0,step=1):
-         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):
-         checkIndex(key)
-         self.changed[key] = value

输出结果

- >>> s[100]
- 201

- >>> s = ArithmeticSequence(1,2)
- >>> s[4]
- 9

- >>> s[10]
- 21

- >>> del s[4]
- Traceback (most recent call last):
-   File "<pyshell#10>", line 1, in <module>
-     del s[4]
- AttributeError: ArithmeticSequence instance has no attribute '__delitem__'

- >>> s['four']
- Traceback (most recent call last):
-   File "<pyshell#11>", line 1, in <module>
-     s['four']
-   File "D:\Learn\Python\Person.py", line 71, in __getitem__
-     checkIndex(key)
-   File "D:\Learn\Python\Person.py", line 62, in checkIndex
-     raise TypeError
- TypeError
- >>> s[-4]
- Traceback (most recent call last):
-   File "<pyshell#12>", line 1, in <module>
-     s[-4]
-   File "D:\Learn\Python\Person.py", line 71, in __getitem__
-     checkIndex(key)
-   File "D:\Learn\Python\Person.py", line 64, in checkIndex
-     raise IndexError
- IndexError

子类化列表,字典和字符串

三个关于序列和映射规则(UserList,UserString,UserDict),如果希望实现一个和内建列表行为相似的序列,可以使用子类list,看看下面的例子,带有访问计数的列表

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

-     def __getitem__(self,index):
-         self.counter +=1

-         return super(CounterList,self).__getitem__(index)

#下面是她如何使用的一些例子

- >>> c = CounterList(range(10))
- >>> c
- [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- >>> c.reverse()
- >>> c
- [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
- >>> del c[3:6]
- >>> c
- [9, 8, 7, 3, 2, 1, 0]
- >>> c.counter
- 0

- >>> c[4]+c[2]
- 9

- >>> c.counter
- 2

#其他跟list性能一样,但有个counter特性,每次执行加法后会自增。

 

属性:

- class Rectangle:
-     def __init__(self):
-         self.width  = 0

-         self.height = 0

-     def setSize(self,size):
-         self.width,self.height = size
-     def getSize(self):
-         return self.width,self.height
- >>> r= Rectangle()
- >>> r.width = 10

- >>> r.height= 5

- >>> r.getSize()
- (10, 5)
- >>> r.setSize((150,100))
- >>> r.width
- 150

 

property函数:

- __metaclass__=type
- class Rectangle:
-     def __init__(self):
-         self.width  = 0

-         self.height = 0

-     def setSize(self,size):
-         self.width,self.height = size
-     def getSize(self):
-         return self.width,self.height
-     size = property(getSize,setSize)

在这个新版的Rectangle中,property函数创建了一个属性,其中访问器函数被用做参数(先是取值,然后是赋值),这个属性命为size,这样一来,就不用担心是如何实现的,可以用同样的方式处理width,height和size.

- >>> r = Rectangle()
- >>> r.width = 10

- >>> r.height= 20

- >>> r.size
- (10, 20)
- >>> r.size = 100,200

- >>> r.width
- 100

property函数可以用0,1,2,3或4个参数来调用。如果没有参数,产生的属性即不可读,也不可写。如果只使用一个参数调用,产生的属性是只读的第3个参数。名字分别叫:fget,fset,fdel,doc__

<span style="font-family: SimHei; font-size: 18px;"--<----

静态方法和类成员方法

静态方法和类成员方法分别在创建时被装入Staticmethod类型和Classmethod类型的对象中。

静态方法的定义没有self参数,且可能被类本身直接调用。

类方法在定义时需要名为cls的类似于self的参数,类成员方法可以直接用类的具体对象调用。但cls参数是自动被绑定类的。看下例子:

- class MyClass:
-     def smeth():
-         print 'This is a stacie method'

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

-     cmeth = classmethod(cmeth)

#用@来替代

- __metaclass__ = type
- class MyClass:
-     @staticmethod

-     def smeth():
-         print 'This is a stacie method'

-     @classmethod

-     def cmeth(cls):
-         print 'This is a class method of', cls

#定义好了方法后,可以这样调用。

- >>> MyClass.smeth()
- This is a stacie method
- >>> MyClass.cmeth()
- This is a class method of <class '__main__.MyClass'>

<span style="color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); font-family: SimHei; font-size: 18px;"--<----

__getattr__,__setattr__

为了访问特性的时候可以执行代码,必须使用一些魔法方法。下面四种方法提供了需要的功能。

__getattrbute__(self,name): 当特性name被访问时,自动被调用

__getattr__(self,name):当特性name被访问,且对象没有相应的特性时被自动调用

__setattr__(self,name,value):当试图给特性name赋值时会被自动调用

__delattr__(self,name): 当试图删除特性name时被自动调用。

- class Rectangle:
-     def __init__(self):
-         self.width  = 0

-         self.height = 0

-     def __setattr__(self,name,value):
-         if name == 'size':
-             self.width,self.height = value
-         else:
-             self.__dict__[name] = value
-     def __getattr__(self,name):
-         if name == 'size':
-             return self.width,self.height
-         else:
-             raise AttributeError

 

迭代器:

主要讨论特殊的方法:__iter__这个迭代器规则的基础.__iter__方法返回一个迭代器,所谓迭代器就是具有next方法的对象。如果next被调用,却没有值可以返回,则会返回StopIteration的异常.为什么要用迭代器而不用列表呢?

如果值很多,列表一次性获得,会占用太多的内存。而迭代则可以一个一个获取。还有为什么要用迭代器的原因:

迭代器更通用,更简单,更优雅。

- 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

首先是实现了__iter__方法,这个方法实际上返回迭代器本身。很多情况下,__iter__会放到for循环中使用的对象中.首先产生一个Fibs对象

>>> fibs = Fibs()

其次可在for循环中使用该对象-比如去查找在斐波那契数列中比1000大的数中的最小的数:

- >>> for f in fibs:
- if f>1000:
- print f
- break

-

- 1597

Note: 内建函数iter可以从可迭代的对象中获得迭代器.

- >>> it = iter([1,2,3])
- >>> it.next()
- 1

- >>> it.next()
- 2

除此之外,它也可以从函数或者其他可调用对象中获取可迭代对象.

 

从迭代器中得到序列:

除了再迭代器和可迭代对象上进行迭代外,还能把它们转换为序列。在大部分能使用序列的情况下,能使用迭代器替换。一个很有用的例子是使用list构造方法显式地将迭代器转化为列表

- class TestIter:
-     value = 0

-     def next(self):
-         self.value +=1

-         if self.value > 10 :
-             raise StopIteration
-         return self.value
-     def __iter__(self):

输出结果:

- >>> ti = TestIter()
- >>> ti
- <__main__.TestIter instance at 0x0000000002A81A08>
- >>> list(ti)
- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  1.  

 

生成器: 是Python新引入的概念,由于历史原因,它也叫简单生成器。它和迭代器可能是近几年来引入的最强大的两个特性。生成器可以帮助程序员写出非常优雅的代码,当然编写任何程序也不可以不使用。生成器是一种用普通的函数语法定义的迭代器。

 

创建生成器:

nested = [[1,2],[3,4],[5]]如果像这么个例子,怎么样用将数字一一打印出来。

- def flatten(nested):
-     for sublist in nested:
-         for element in sublist:
-             yield element

#如何包含yield的语句,称为生成器

- >>> nested = [[1],[2,3],[4,5,6]]
- >>> flatten(nested)
- <generator object flatten at 0x00000000020635A0>
- >>> for num in flatten(nested):
-         print num
- 1

- 2

- 3

- 4

- 5

- 6

 

递归生成器:

上一个例子创建的生成器只能处理两层嵌套,使用了两个for循环。如果要处理任意层的嵌套怎么办?就应该更灵活,现在就到了递归生成器登场的时候了。

- def flatten(nested):
-     try:
-         for sublist in nested:
-             for element in flatten(sublist):
-                 yield element
-     except TypeError:
-         yield nested

两种情况: 基本情况和需要递归的情况

1. 如果只是元素,函数被告知展开一个元素,这种情况下,for循环会引发一个TypeError异常,生成器会产生一个元素.

2. 如果是一个列表,那么就要进行特殊处理。程序必须遍历所有子列表,并对它们调用flatten,然后使用另一个for循环来产生被展开的子列表中的所有元素,很神奇吧。

>>> nested = [[[[1,2],3],4],5]
>>> list(flatten(nested))
[1, 2, 3, 4, 5]

如果是字符串对象,那么它就是一个序列,不会引发TypeError,如果你不想对这样的对象进行迭代。为了处理这种情况,则必须在生成器的开始处添加一个检查语句。试着将传入的对象和一个字符串拼接,看看会不会出现TypeError,这是检查一个对象是不是类似于字符串的最简单,最快速的方法。下面加入检查语句的生成器。

>>> nested=['a',[[[1,2],3],4],5]
>>> list(flatten(nested))
['a', 1, 2, 3, 4, 5]
------

通用生成器:

生成器是一个包含yield关键字的函数。当它被调用时,在函数体中的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,知道遇到一个yield或者return语句。yield意味着应该生成一个值。return语句意味着生成器要停止执行。换句话说,生成器由两部分组成:生成器的函数和生成器的迭代器。生成器的函数是用def语句定义,包含yield部分,生成器的迭代器是这个函数返回的部分。

- >>> def simple_generator():
- yield 1

-

- >>> simple_generator
- <function simple_generator at 0x0000000002AFB0B8>
- >>> simple_generator()
- <generator object simple_generator at 0x00000000027C8EE8>
- >>>

<span style="font-size: 18.6667px; font-family: SimHei;"--<----

生成器方法:

- >>> def repeater(value):
-          while True:
-              new = (yield value)
-              if new is not None: value = new

输出结果:

- >>> r = repeater(42)
- >>> r.next()
- 42

- >>> r.next()
- 42

- >>> r.send('Hello,World!')
- 'Hello,World!'

#next()方法,send()方法,throw()方法,close()方法

<span style="font-size: 18.6667px;"--<----

模拟生成器:

如何使用普通的函数模拟生成器

首先将下面语句放在程序的开始处

result = []

然后将下面这种形式的代码:

yield some_expression

用下面的语句替换:

result.append(some_expression)

最后在函数的末尾,添加下面的语句: 

return result

下面是flatten生成器用普通的函数重写的版本

- def flatten(nested):
-     result = []
-     try:
-         try: nested + ''

-         except TypeError: pass

-         else: raise TypeError
-         for sublist in nested:
-             for element in flatten(sublist):
-                 result.append(element)
-     except TypeError:
-         result.append(nested)
-     return result

输出结果:

- >>> n = [[[[[['HaHa...'],1],2],3],4],5]
- >>> flatten(n)
- ['HaHa...', 1, 2, 3, 4, 5]

<span style="font-family: SimHei; font-size: 19px;"--<----

本章新函数

iter(obj)                            从一个可迭代的对象得到迭代器

property(fget,fset,fdel,doc)         返回一个属性,所有参数都是可选的

super(class,obj)                     返回一个类的超类的绑定实例

打赏
赞(0) 打赏
未经允许不得转载:同乐学堂 » Day9-人生苦短我学python

特别的技术,给特别的你!

联系QQ:1071235258QQ群:710045715

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

error: Sorry,暂时内容不可复制!