__new__ 是类方法, __init__ 是实例方法;
重载 __new__ 方法,需要返回类的实例;
一般情况下,我们不需要重载 __new__ 方法。但在某些情况下,我们想控制实例的创建过程,这
时可以通过重载 __new_ 方法来实现。
让我们看一个例子:
1. class A ( object ):
2. _dict = dict ()
4. def __new__ ( cls ):
5. if ‘key’ in A . _dict :
6. print “EXISTS”
7. return A . _dict [ ‘key’ ]
8. else :
9. print “NEW”
10. return object . __new__ ( cls )
12. def __init__ ( self ):
13. print “INIT”
14. A . _dict [ ‘key’ ] = self
在上面,我们定义了一个类 A ,并重载了 __new__ 方法:当 key 在 A._dict 中
时,直接返回 A._dict[‘key’] ,否则创建实例。
执行情况:
1. >>> a1 = A ()
2. NEW
3. INIT
4. >>> a2 = A ()
5. EXISTS
6. INIT
7. >>> a3 = A ()
8. EXISTS
9. INIT
5.2 str & repr
先看一个简单的例子:
1. class Foo ( object ):
2. def __init__ ( self , name ):
3. self . name = name
5. >>> print Foo ( ‘ethan’ )
6. < __main__ . Foo object at 0x10c37aa50 >
在上面,我们使用 print 打印一个实例对象,但如果我们想打印更多信息呢,比如把 name 也打印
出来,这时,我们可以在类中加入 __str__ 方法,如下:
1. class Foo ( object ):
2. def __init__ ( self , name ):
3. self . name = name
4. def __str__ ( self ):
5. return ‘Foo object (name: %s)’ % self . name
7. >>> print Foo(‘ethan’) # 使用 print
8. Foo object ( name : ethan )
9. >>>
10. >>> str ( Foo ( ‘ethan’ )) # 使用 str
11. ‘Foo object (name: ethan)’
12. >>>
13. >>> Foo ( ‘ethan’ ) # 直接显示
14. < __main__ . Foo at 0x10c37a490 >
可以看到,使用 print 和 str 输出的是 __str__ 方法返回的内容,但如果直接显示则不是,
那能不能修改它的输出呢?当然可以,我们只需在类中加入 __repr__ 方法,比如:
1. class Foo ( object ):
2. def __init__ ( self , name ):
3. self . name = name
4. def __str__ ( self ):
5. return ‘Foo object (name: %s)’ % self . name
6. def __repr__ ( self ):
7. return ‘Foo object (name: %s)’ % self . name
9. >>> Foo ( ‘ethan’ )
10. ‘Foo object (name: ethan)’
可以看到,现在直接使用 Foo(‘ethan’) 也可以显示我们想要的结果了,然而,我们发现上面的代
码中, __str__ 和 __repr__ 方法的代码是一样的,能不能精简一点呢,当然可以,如下:
1. class Foo ( object ):
2. def __init__ ( self , name ):
3. self . name = name
4. def __str__ ( self ):
5. return ‘Foo object (name: %s)’ % self . name
6. __repr__ = __str__
5.3 iter
在某些情况下,我们希望实例对象可被用于 for…in 循环,这时我们需要在类中定义
__iter__ 和 next (在 Python3 中是 __next__ )方法,其中, __iter__ 返回一
个迭代对象, next 返回容器的下一个元素,在没有后续元素时抛出 StopIteration 异常。
看一个斐波那契数列的例子:
1. class Fib ( object ):
2. def __init__ ( self ):
3. self . a , self . b = 0 , 1
5. def __iter__ ( self ): # 返回迭代器对象本身
6. return self
8. def next ( self ): # 返回容器下一个元素
9. self . a , self . b = self . b , self . a + self . b
10. return self . a
12. >>> fib = Fib ()
13. >>> for i in fib :
14. … if i > 10 :
15. … break
16. … print i
17. …
18. 1
19. 1
20. 2
21. 3
22. 5
23. 8
5.4 getitem
有时,我们希望可以使用 obj[n] 这种方式对实例对象进行取值,比如对斐波那契数列,我们希望
可以取出其中的某一项,这时我们需要在类中实现 __getitem__ 方法,比如下面的例子:
1. class Fib ( object ):
2. def __getitem__ ( self , n ):
3. a , b = 1 , 1
4. for x in xrange ( n ):
5. a , b = b , a + b
6. return a
8. >>> fib = Fib ()
9. >>> fib [ 0 ], fib [ 1 ], fib [ 2 ], fib [ 3 ], fib [ 4 ], fib [ 5 ]
10. ( 1 , 1 , 2 , 3 , 5 , 8 )
我们还想更进一步,希望支持 obj[1:3] 这种切片方法来取值,这时 __getitem__ 方法传入的参数可能是一个整数,也可能是一个切片对象 slice ,因此,我们需要对传入的参数进行判断,可
以使用 isinstance 进行判断,改后的代码如下:
1. class Fib ( object ):
2. def __getitem__ ( self , n ):
3. if isinstance ( n , slice ): # 如果 n 是 slice 对象
4. a , b = 1 , 1
5. start , stop = n . start , n . stop
6. L = []
7. for i in xrange ( stop ):
8. if i >= start :
9. L . append ( a )
10. a , b = b , a + b
11. return L
12. if isinstance ( n , int ): # 如果 n 是 int 型
13. a , b = 1 , 1
14. for i in xrange ( n ):
15. a , b = b , a + b
16. return a
现在,我们试试用切片方法:
1. >>> fib = Fib ()
2. >>> fib [ 0 : 3 ]
3. [ 1 , 1 , 2 ]
4. >>> fib [ 2 : 6 ]
5. [ 2 , 3 , 5 , 8 ]
上面,我们只是简单地演示了 getitem 的操作,但是它还很不完善,比如没有对负数处理,不支持带
step 参数的切片操作 obj[1:2:5] 等等,读者有兴趣的话可以自己实现看看。
__geitem__ 用于获取值,类似地, __setitem__ 用于设置值, __delitem__ 用于删除
值,让我们看下面一个例子:
1. class Point ( object ):
2. def __init__ ( self ):
3. self . coordinate = {}
5. def __str__ ( self ):
6. return “point(%s)” % self . coordinate
8. def __getitem__ ( self , key ):
9. return self . coordinate . get ( key )
11. def __setitem__ ( self , key , value ):
12. self . coordinate [ key ] = value
14. def __delitem__ ( self , key ):
15. del self . coordinate [ key ]
16. print ‘delete %s’ % key
18. def __len__ ( self ):
19. return len ( self . coordinate )
21. __repr__ = __str__
在上面,我们定义了一个 Point 类,它有一个属性 coordinate (坐标),是一个字典,让我们看
看使用:
1. >>> p = Point ()
2. >>> p [ ‘x’ ] = 2 # 对应于 p.__setitem__(‘x’, 2)
3. >>> p [ ‘y’ ] = 5 # 对应于 p.__setitem__(‘y’, 5)
4. >>> p # 对应于 __repr__
5. point ({ ‘y’ : 5 , ‘x’ : 2 })
6. >>> len ( p ) # 对应于 p.__len__
7. 2
8. >>> p [ ‘x’ ] # 对应于 p.__getitem__(‘x’)
9. 2
10. >>> p [ ‘y’ ] # 对应于 p.__getitem__(‘y’)
11. 5
12. >>> del p [ ‘x’ ] # 对应于 p.__delitem__(‘x’)
13. delete x
14. >>> p
15. point ({ ‘y’ : 5 })
16. >>> len ( p )
17. 1
5.5 getattr
当我们获取对象的某个属性,如果该属性不存在,会抛出 AttributeError 异常,比如:
1. class Point ( object ):
2. def __init__ ( self , x = 0 , y = 0 ):
3. self . x = x
4. self . y = y
6. >>> p = Point ( 3 , 4 )
7. >>> p . x , p . y
8. ( 3 , 4 )
9. >>> p . z
10. ---------------------------------------------------------------------------
11. AttributeError Traceback ( most recent call last )
12. < ipython - input - 547 - 6dce4e43e15c > in < module >()
13. ----> 1 p . z
15. AttributeError : ‘Point’ object has no attribute ‘z’
那有没有办法不让它抛出异常呢?当然有,只需在类的定义中加入 __getattr__ 方法,比如:
1. class Point ( object ):
2. def __init__ ( self , x = 0 , y = 0 ):
3. self . x = x
4. self . y = y
5. def __getattr__ ( self , attr ):
6. if attr == ‘z’ :
7. return 0
9. >>> p = Point ( 3 , 4 )
10. >>> p . z
11. 0
现在,当我们调用不存在的属性(比如 z )时,解释器就会试图调用 __getattr__(self, ‘z’)
来获取值,但是,上面的实现还有一个问题,当我们调用其他属性,比如 w ,会返回 None ,因为
__getattr__ 默认返回就是 None ,只有当 attr 等于 ‘z’ 时才返回 0 ,如果我们想让
__getattr__ 只响应几个特定的属性,可以加入异常处理,修改 __getattr__ 方法,如下:
1. def __getattr__ ( self , attr ):
2. if attr == ‘z’ :
3. return 0
4. raise AttributeError ( “Point object has no attribute %s” % attr )
这里再强调一点, __getattr__ 只有在属性不存在的情况下才会被调用,对已存在的属性不会调用
__getattr__ 。
与 __getattr__ 一起使用的还有 __setattr__ , __delattr__ ,类似 obj.attr =value , del obj.attr ,看下面一个例子:
1. class Point ( object ):
2. def __init__ ( self , x = 0 , y = 0 ):
3. self . x = x
4. self . y = y
6. def __getattr__ ( self , attr ):
7. if attr == ‘z’ :
8. return 0
9. raise AttributeError ( “Point object has no attribute %s” % attr )
11. def __setattr__ ( self , * args , ** kwargs ):
12. print ‘call func set attr (%s, %s)’ % ( args , kwargs )
13. return object . __setattr__ ( self , * args , ** kwargs )
15. def __delattr__ ( self , * args , ** kwargs ):
16. print ‘call func del attr (%s, %s)’ % ( args , kwargs )
17. return object . __delattr__ ( self , * args , ** kwargs )
19. >>> p = Point ( 3 , 4 )
20. call func set attr (( ‘x’ , 3 ), {})
21. call func set attr (( ‘y’ , 4 ), {})
22. >>> p . z
23. 0
24. >>> p . z = 7
25. call func set attr (( ‘z’ , 7 ), {})
26. >>> p . z
27. 7
28. >>> p . w
29. Traceback ( most recent call last ):
30. File “” , line 1 , in < module >
31. File “” , line 8 , in __getattr__
32. AttributeError : Point object has no attribute w
33. >>> p . w = 8
34. call func set attr (( ‘w’ , 8 ), {})
35. >>> p . w
36. 8
37. >>> del p . w
38. call func del attr (( ‘w’ ,), {})
39. >>> p . __dict__
40. { ‘y’ : 4 , ‘x’ : 3 , ‘z’ : 7 }
5.6 call
我们一般使用 obj.method() 来调用对象的方法,那能不能直接在实例本身上调用呢?在
Python 中,只要我们在类中定义 __call__ 方法,就可以对实例进行调用,比如下面的例子:
1. class Point ( object ):
2. def __init__ ( self , x , y ):
3. self . x , self . y = x , y
4. def __call__ ( self , z ):
5. return self . x + self . y + z
使用如下:
1. >>> p = Point ( 3 , 4 )
2. >>> callable ( p ) # 使用 callable 判断对象是否能被调用
3. True
4. >>> p ( 6 ) # 传入参数,对实例进行调用,对应 p.__call__(6)
5. 13 # 3+4+6
可以看到,对实例进行调用就好像对函数调用一样。
__new__ 在 __init__ 之前被调用,用来创建实例。
__str__ 是用 print 和 str 显示的结果, __repr__ 是直接显示的结果。
__getitem__ 用类似 obj[key] 的方式对对象进行取值
__getattr__ 用于获取不存在的属性 obj.attr
__call__ 使得可以对实例进行调用
5.7 小结
__new__ 在 __init__ 之前被调用,用来创建实例。
__str__ 是用 print 和 str 显示的结果, __repr__ 是直接显示的结果。
__getitem__ 用类似 obj[key] 的方式对对象进行取值
__getattr__ 用于获取不存在的属性 obj.attr
__call__ 使得可以对实例进行调用
6 slots 魔法
在 Python 中,我们在定义类的时候可以定义属性和方法。当我们创建了一个类的实例后,我们还可以给该实例绑定任意新的属性和方法。
看下面一个简单的例子:
1. class Point ( object ):
2. def __init__ ( self , x = 0 , y = 0 ):
3. self . x = x
4. self . y = y
6. >>> p = Point ( 3 , 4 )
7. >>> p . z = 5 # 绑定了一个新的属性
8. >>> p . z
9. 5
10. >>> p . __dict__
11. { ‘x’ : 3 , ‘y’ : 4 , ‘z’ : 5 }
在上面,我们创建了实例 p 之后,给它绑定了一个新的属性 z ,这种动态绑定的功能虽然很有用,但它的代价是消耗了更多的内存。
因此,为了不浪费内存,可以使用 __slots__ 来告诉 Python 只给一个固定集合的属性分配空
间,对上面的代码做一点改进,如下:
1. class Point ( object ):
2. __slots__ = ( ‘x’ , ‘y’ ) # 只允许使用 x 和 y
4. def __init__ ( self , x = 0 , y = 0 ):
5. self . x = x
6. self . y = y
上面,我们给 __slots__ 设置了一个元组,来限制类能添加的属性。现在,如果我们想绑定一个
新的属性,比如 z ,就会出错了,如下:
1. >>> p = Point ( 3 , 4 )
2. >>> p . z = 5
3. ---------------------------------------------------------------------------
4. AttributeError Traceback ( most recent call last )
5. < ipython - input - 648 - 625ed954d865 > in < module >()
6. ----> 1 p . z = 5
8. AttributeError : ‘Point’ object has no attribute ‘z’
使用 __slots__ 有一点需要注意的是, __slots__ 设置的属性仅对当前类有效,对继承的子
类不起效,除非子类也定义了 __slots__ ,这样,子类允许定义的属性就是自身的 slots 加上父
类的 slots 。
7 使用 @property
在使用 @property 之前,让我们先来看一个简单的例子:
1. class Exam ( object ):
2. def __init__ ( self , score ):
3. self . _score = score
5. def get_score ( self ):
6. return self . _score
8. def set_score ( self , val ):
9. if val < 0 :
10. self . _score = 0
11. elif val > 100 :
12. self . _score = 100
13. else :
14. self . _score = val
16. >>> e = Exam ( 60 )
17. >>> e . get_score ()
18. 60
19. >>> e . set_score ( 70 )
20. >>> e . get_score ()
21. 70
在上面,我们定义了一个 Exam 类,为了避免直接对 _score 属性操作,我们提供了
get_score 和 set_score 方法,这样起到了封装的作用,把一些不想对外公开的属性隐蔽起来,
而只是提供方法给用户操作,在方法里面,我们可以检查参数的合理性等。
这样做没什么问题,但是我们有更简单的方式来做这件事, Python 提供了 property 装饰器,被
装饰的方法,我们可以将其『当作』属性来用,看下面的例子:
1. class Exam ( object ):
2. def __init__ ( self , score ):
3. self . _score = score
5. @property
6. def score ( self ):
7. return self . _score
9. @score . setter
10. def score ( self , val ):
11. if val < 0 :
12. self . _score = 0
13. elif val > 100 :
14. self . _score = 100
15. else :
16. self . _score = val
18. >>> e = Exam ( 60 )
19. >>> e . score
20. 60
21. >>> e . score = 90
22. >>> e . score
23. 90
24. >>> e . score = 200
25. >>> e . score
26. 100
在上面,我们给方法 score 加上了 @property ,于是我们可以把 score 当成一个属性来用,
此时,又会创建一个新的装饰器 score.setter ,它可以把被装饰的方法变成属性来赋值。
另外,我们也不一定要使用 score.setter 这个装饰器,这时 score 就变成一个只读属性了:
1. class Exam ( object ):
2. def __init__ ( self , score ):
3. self . _score = score
5. @property
6. def score ( self ):
7. return self . _score
9. >>> e = Exam ( 60 )
10. >>> e . score
11. 60
12. >>> e . score = 200 # score 是只读属性,不能设置值
13. ---------------------------------------------------------------------------
14. AttributeError Traceback ( most recent call last )
15. < ipython - input - 676 - b0515304f6e0 > in < module >()
16. ----> 1 e . score = 200
18. AttributeError : can 't set attribute
@property 把方法『变成』了属性 。
8 你不知道的 super
在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super 来实现,比如:
1. class Animal ( object ):
2. def __init__ ( self , name ):
3. self . name = name
4. def greet ( self ):
5. print ‘Hello, I am %s.’ % self . name
7. class Dog ( Animal ):
8. def greet ( self ):
9. super ( Dog , self ). greet () # Python3 可使用 super().greet()
10. print ‘WangWang…’
在上面, Animal 是父类, Dog 是子类,我们在 Dog 类重定义了 greet 方法,为了能同时实现
父类的功能,我们又调用了父类的方法,看下面的使用:
1. >>> dog = Dog ( ‘dog’ )
2. >>> dog . greet ()
3. Hello , I am dog .
4. WangWang …
super 的一个最常见用法可以说是在子类中调用父类的初始化方法了,比如:
1. class Base ( object ):
2. def __init__ ( self , a , b ):
3. self . a = a
4. self . b = b
6. class A ( Base ):
7. def __init__ ( self , a , b , c ):
8.
super ( A , self ). __init__ ( a , b ) # Python3 可使用 super().__init__(a, b)
9. self . c = c
8.1 深入 super()
看了上面的使用,你可能会觉得 super 的使用很简单,无非就是获取了父类,并调用父类的方 法。其实,在上面的情况下, super 获得的类刚好是父类,但在其他情况就不一定了, super 其实和
父类没有实质性的关联。
让我们看一个稍微复杂的例子,涉及到多重继承,代码如下:
1. class Base ( object ):
2. def __init__ ( self ):
3. print “enter Base”
4. print “leave Base”
6. class A ( Base ):
7. def __init__ ( self ):
8. print “enter A”
9. super ( A , self ). __init__ ()
10. print “leave A”
12. class B ( Base ):
13. def __init__ ( self ):
14. print “enter B”
15. super ( B , self ). __init__ ()
16. print “leave B”
18. class C ( A , B ):
19. def __init__ ( self ):
20. print “enter C”
21. super ( C , self ). __init__ ()
22. print “leave C”
其中, Base 是父类, A, B 继承自 Base, C 继承自 A, B ,它们的继承关系是一个典型的『菱形继
承』,如下:
1. Base
2. / \
3. / \
4. A B
5. \ /
6. \ /
7. C
现在,让我们看一下使用:
1. >>> c = C ()
2. enter C
3. enter A
4. enter B
5. enter Base
6. leave Base
7. leave B
8. leave A
9. leave C
如果你认为 super 代表『调用父类的方法』,那你很可能会疑惑为什么 enter A 的下一句不是
enter Base 而是 enter B 。原因是, super 和父类没有实质性的关联,现在让我们搞清
super 是怎么运作的。
8.2 MRO 列表
事实上,对于你定义的每一个类, Python 会计算出一个方法解析顺序( Method Resolution
Order, MRO )列表,它代表了类继承的顺序,我们可以使用下面的方式获得某个类的 MRO 列表:
1. >>> C . mro () # or C.__mro__ or C().__class__.mro()
2. [ __main__ . C , __main__ . A , __main__ . B , __main__ . Base , object ]
那这个 MRO 列表的顺序是怎么定的呢,它是通过一个 C3 线性化算法 来实现的,这里我们就不去深究
这个算法了,感兴趣的读者可以自己去了解一下,总的来说,一个类的 MRO 列表就是合并所有父类的
MRO 列表,并遵循以下三条原则:
子类永远在父类前面
如果有多个父类,会根据它们在列表中的顺序被检查
如果对下一个类存在两个合法的选择,选择第一个父类
8.3 super 原理
super 的工作原理如下:
1. def super ( cls , inst ):
2. mro = inst . __class__ . mro ()
3. return mro [ mro . index ( cls ) + 1 ]
其中, cls 代表类, inst 代表实例,上面的代码做了两件事:
获取 inst 的 MRO 列表
查找 cls 在当前 MRO 列表中的 index, 并返回它的下一个类,即 mro[index + 1]
当你使用 super(cls, inst) 时, Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类。
现在,让我们回到前面的例子。
首先看类 C 的 __init__ 方法:
1. super ( C , self ). __init__ ()
这里的 self 是当前 C 的实例, self. class .mro() 结果是:
1. [ __main__ . C , __main__ . A , __main__ . B , __main__ . Base , object ]
可以看到, C 的下一个类是 A ,于是,跳到了 A 的 __init__ ,这时会打印出 enter A ,并执
行下面一行代码:
1. super ( A , self ). __init__ ()
注意,这里的 self 也是当前 C 的实例, MRO 列表跟上面是一样的,搜索 A 在 MRO 中的下一个
类,发现是 B ,于是,跳到了 B 的 __init__ ,这时会打印出 enter B ,而不是 enter
Base 。
整个过程还是比较清晰的,关键是要理解 super 的工作方式,而不是想当然地认为 super 调用了父
类的方法。
事实上, super 和父类没有实质性的关联。
super(cls, inst) 获得的是 cls 在 inst 的 MRO 列表中的下一个类
8.4 陌生的 metaclass
Python 中的元类( metaclass )是一个深度魔法,平时我们可能比较少接触到元类,本文将通过一些简单的例子来理解这个魔法。
在 Python 中,一切皆对象。字符串,列表,字典,函数是对象,类也是一个对象,因此你可以:
把类赋值给一个变量
把类作为函数参数进行传递
把类作为函数的返回值
在运行时动态地创建类
看一个简单的例子:
1. class Foo ( object ):
2. foo = True
4. class Bar ( object ):
5. bar = True
7. def echo ( cls ):
8. print cls
10. def select ( name ):
11. if name == ‘foo’ :
12. return Foo # 返回值是一个类
13. if name == ‘bar’ :
14. return Bar
16. >>> echo ( Foo ) # 把类作为参数传递给函数 echo
17. < class ‘__main__.Foo’ >
18. >>> cls = select ( ‘foo’ ) # 函数 select 的返回值是一个类,把它赋给变量 cls
19. >>> cls
20. __main__ . Foo
8.5 熟悉又陌生的 type
在日常使用中,我们经常使用 object 来派生一个类,事实上,在这种情况下, Python 解释器会
调用 type 来创建类。
这里,出现了 type ,没错,是你知道的 type ,我们经常使用它来判断一个对象的类型,比
如:
1. class Foo ( object ):
2. Foo = True
4. >>> type ( 10 )
5. < type ‘int’ >
6. >>> type ( ‘hello’ )
7. < type ‘str’ >
8. >>> type ( Foo ())
9. < class ‘__main__.Foo’ >
10. >>> type ( Foo )
11. < type ‘type’ >
事实上, type 除了可以返回对象的类型,它还可以被用来动态地创建类(对象)。下面,我们看
几个例子,来消化一下这句话。
使用 type 来创建类(对象)的方式如下:
type( 类名 , 父类的元组(针对继承的情况,可以为空),包含属性和方法的字典(名称和值) )
8.6 最简单的情况
假设有下面的类:
1. class Foo ( object ):
2. pass
现在,我们不使用 class 关键字来定义,而使用 type ,如下:
1. Foo = type ( ‘Foo’ , ( object , ), {}) # 使用 type 创建了一个类对象
上面两种方式是等价的。我们看到, type 接收三个参数:
第 1 个参数是字符串 ‘Foo’ ,表示类名
第 2 个参数是元组 (object, ) ,表示所有的父类
第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法。
在上面,我们使用 type() 创建了一个名为 Foo 的类,然后把它赋给了变量 Foo ,我们当然可
以把它赋给其他变量,但是,此刻没必要给自己找麻烦。
接着,我们看看使用:
1. >>> print Foo
2. < class ‘__main__.Foo’ >
3. >>> print Foo ()
4. < __main__ . Foo object at 0x10c34f250 >
假设有下面的类:
1. class Foo ( object ):
2. foo = True
3. def greet ( self ):
4. print ‘hello world’
5. print self . foo
用 type 来创建这个类,如下:
1. def greet ( self ):
2. print ‘hello world’
3. print self . foo
5. Foo = type ( ‘Foo’ , ( object , ), { ‘foo’ : True , ‘greet’ : greet })
上面两种方式的效果是一样的,看下使用:
1. >>> f = Foo ()
2. >>> f . foo
3. True
4. >>> f . greet
5. < bound method Foo . greet of < __main__ . Foo object at 0x10c34f890 >>
6. >>> f . greet ()
7. hello world
8. True
8.7 继承的情况
再来看看继承的情况,假设有如下的父类:
1. class Base ( object ):
2. pass
我们用 Base 派生一个 Foo 类,如下:
1. class Foo ( Base ):
2. foo = True
改用 type 来创建,如下:
1. Foo = type ( ‘Foo’ , ( Base , ), { ‘foo’ : True })
9 元类
9.1 概念
元类( metaclass )是用来创建类(对象)的可调用对象。这里的可调用对象可以是函数或者类等。但一般情况下,我们使用类作为元类。对于实例对象、类和元类,我们可以用下面的图来描述:
我们在前面使用了 type 来创建类(对象),事实上, type 就是一个元类。
那么,元类到底有什么用呢?要你何用 …
元类的主要目的是为了控制类的创建行为。我们还是先来看看一些例子,以消化这句话。
9.2 元类的使用
先从一个简单的例子开始,假设有下面的类:
1. class Foo ( object ):
2. name = ‘foo’
3. def bar ( self ):
4. print ‘bar’
现在我们想给这个类的方法和属性名称前面加上 my_ 前缀,即 name 变成 my_name , bar 变成
my_bar ,另外,我们还想加一个 echo 方法。当然,有很多种做法,这里展示用元类的做法。
- 首先,定义一个元类,按照默认习惯,类名以 Metaclass 结尾,代码如下:
1. class PrefixMetaclass ( type ):
2. def __new__ ( cls , name , bases , attrs ):
3. # 给所有属性和方法前面加上前缀 my_
4. _attrs = (( ‘my_’ + name , value ) for name , value in attrs . items ())
6. _attrs = dict (( name , value ) for name , value in _attrs ) # 转化为字典
7. _attrs [ ‘echo’ ] = lambda self , phrase : phrase # 增加了一个 echo 方法
9. return type . __new__ ( cls , name , bases , _attrs ) # 返回创建后的类
上面的代码有几个需要注意的点:
最后
Python崛起并且风靡,因为优点多、应用领域广、被大牛们认可。学习 Python 门槛很低,但它的晋级路线很多,通过它你能进入机器学习、数据挖掘、大数据,CS等更加高级的领域。Python可以做网络应用,可以做科学计算,数据分析,可以做网络爬虫,可以做机器学习、自然语言处理、可以写游戏、可以做桌面应用…Python可以做的很多,你需要学好基础,再选择明确的方向。这里给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!
👉Python所有方向的学习路线👈
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
👉Python必备开发工具👈
工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。
👉Python全套学习视频👈
我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。
👉实战案例👈
学python就与学数学一样,是不能只看书不做题的,直接看步骤和答案会让人误以为自己全都掌握了,但是碰到生题的时候还是会一筹莫展。
因此在学习python的过程中一定要记得多动手写代码,教程只需要看一两遍即可。
👉大厂面试真题👈
我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0