1. super([type[,object-or-type2]])
返回一个代理对象,该对象将方法调用委托给类型的父类或同级类。这对于访问已在类中重写的继承方法非常有用。搜索顺序与getattr()使用的顺序相同,只是跳过了类型本身。
类型的__mro__属性列出getattr()和super()使用的方法解析搜索顺序。该属性是动态的,可以在更新继承层次结构时更改。
如果省略第二个参数,返回的super对象将被解除绑定。如果第二个参数是一个对象(object),则isinstance(obj, type)必须为真。如果第二个参数是一个类型(type),issubclass(type2, type)必须为真(这对于类方法很有用)。
super有两个典型的用例。在单继承的类层次结构中,可以使用super引用父类而不显式地命名它们,从而使代码更易于维护。这种用法与super在其它编程语言中的用法非常相似。
第二个用例是支持动态执行环境中的协作多重继承。这个用例是Python特有的,在静态编译语言或只支持单继承的语言中是找不到的。这使得实现“菱形图”成为可能,其中多个基类实现相同的方法。好的设计要求该方法在每种情况下都具有相同的调用签名(因为调用顺序是在运行时确定的,因为该顺序适应类层次结构中的更改,并且因为该顺序可以包括运行前未知的同级类)。
注意,super()是作为绑定过程的一部分实现的,用于显式的点式属性查找,如super(). __getitem__(name)。它是通过实现自己的__getattribute__()方法来实现的,该方法用于以支持协作多重继承的可预测顺序来搜索类。因此,对于使用语句或super()[name]等操作符进行隐式查找,super()是未定义的。
还要注意,除了零参数形式之外,super()并不仅限于在方法内部使用。这两种参数形式精确地指定参数并提供适当的引用。零参数形式只在类定义中工作,因为编译器会填充必要的细节,以便正确检索正在定义的类,以及访问普通方法的当前实例。
class A:
def method(self,arg):
print("self_A:",self)
print('A_This is a %s'%str(arg))
def test(self,arg):
print("self_A:",self)
print('A_This is a %s'%str(arg))
def fengye(self,arg):
print("self_A:",self)
print('A_This is a %s'%str(arg))
class B:
def method(self,arg):
print("self_B:",self)
print('B_This is a %s'%str(arg))
def test(self,arg):
print("self_B:",self)
print('B_This is a %s'%str(arg))
def fengye(self,arg):
print("self_B:",self)
print('B_This is a %s'%str(arg))
class C(A,B):
def method(self,arg):
print("self_C:",self)
super().method(arg)
def test(self,arg):
print("self_C:", self)
super(C,self).test(arg)
def fengye(self,arg):
print("self_C:",self)
B.fengye(self,arg)
print('C.__mro__:',C.__mro__)
C().method('dog');print()
C().test('test');print()
C().fengye('fengye')
#运行结果:
C.__mro__: (, , , )
self_C: <__main__.C object at 0x0326F690>
self_A: <__main__.C object at 0x0326F690>
A_This is a dog
self_C: <__main__.C object at 0x0326FA50>
self_A: <__main__.C object at 0x0326FA50>
A_This is a test
self_C: <__main__.C object at 0x0326F6B0>
self_B: <__main__.C object at 0x0326F6B0>
B_This is a fengye
如果您没有被python的super()内置代码所惊艳,那么很可能您并不真正知道它能够做什么或如何有效地使用它。
关于super()已经写了很多,其中很多都是失败的。本文试图改善这种情况通过以下:
使用Python 3语法,让我们从一个基本用例开始,它是一个子类,用于从一个内置类扩展一个方法:
import logging
class LoggingDict(dict):
def __setitem__(self, key, value):
logging.basicConfig(level=logging.INFO)
logging.info('Setting %r to %r' % (key,value))
super().__setitem__(key,value)
ld=LoggingDict()
ld['sex']='男'
这个类具有与其父类dict相同的所有功能,但是它扩展了__setitem__方法,以便在更新键时生成日志条目。在创建日志条目之后,该方法使用super()委托使用键/值对实际更新字典的工作。
在引入super()之前,我们应该用dict.__setitem__(self, key, value)硬连接调用。但是,super()更好,因为它是一个经过计算的间接引用。
间接方法的一个好处是,我们不必按名称指定委托类。如果编辑源代码将基类切换到其他映射,super()引用将自动跟随。你只有一个真相:
class LoggingDict(SomeOtherMapping): # new base class
def __setitem__(self, key, value):
logging.info('Setting %r to %r' % (key, value))
super().__setitem__(key, value) # no change needed
除了隔离更改之外,计算间接寻址还有另一个主要好处,来自静态语言的人可能不熟悉它。由于间接寻址是在运行时计算的,因此我们可以自由地影响计算,以便间接寻址指向其他类。
计算取决于调用super的类和实例的祖先树。第一个组件,调用super的类,由该类的源代码决定。在我们的示例中,在LoggingDict中调用super().__setitem__方法。这个组件是固定的。第二个也是更有趣的组件是variable(我们可以使用丰富的祖先树创建新的子类)。
让我们利用这一优势,在不修改现有类的情况下构造一个日志有序字典:
class LoggingOD(LoggingDict, collections.OrderedDict):
pass
我们新类的祖先树是:LoggingOD、LoggingDict、OrderedDict、dict、object。对于我们的目的,重要的结果是OrderedDict插入到LoggingDict之后和dict之前!这意味着在LoggingDict中调用super().__setitem__将key/value更新分派给OrderedDict,而不是dict。
想一下。我们没有更改LoggingDict的源代码。相反,我们构建了一个子类,其惟一的逻辑是组合两个现有类并控制它们的搜索顺序。
Search Order(搜索顺序)
我所称的搜索顺序或祖先树正式称为方法解析顺序或MRO。通过打印_mro__属性很容易查看MRO:
>>> pprint(LoggingOD.__mro__)
(,
,
,
,
)
如果我们的目标是用我们喜欢的MRO创建一个子类,我们需要知道它是如何计算的。基础很简单。序列包括类、基类和这些基的基类,依此类推,直到到达所有类的根类object。这个序列是有序的,因此一个类总是出现在它的父类之前,如果有多个父类,它们保持与基类的元组相同的顺序。
上面所示的MRO是遵循这些约束的一个顺序:
解决这些约束的过程称为线性化。关于这个主题有很多优秀的论文,但是要创建符合我们喜好的MRO子类,我们只需要知道两个约束条件:孩子优先于他们的父母,并且在__bases__中出现的顺序是受尊重的。
Practical Advice(实用建议)
super()负责将方法调用委托给实例的祖先树中的某个类。要使可重排序方法调用工作,需要协作地设计类。这就提出了三个容易解决的实际问题:
1)让我们首先看看让调用方的参数匹配被调用方法的签名的策略。这比传统方法调用更具挑战性,传统方法调用提前知道被调用方。使用super(),在编写类时不知道被调用者(因为稍后编写的子类可能会将新类引入MRO)。
一种方法是使用位置参数坚持使用固定的签名。这对于像__setitem__这样的方法很有效,它有两个参数(key和value)的固定签名。在LoggingDict示例中显示了这种技术,其中在LoggingDict和dict中,__setitem__具有相同的签名。
更灵活的方法是让祖先树中的每个方法协同设计以接受关键字参数和关键字参数字典,删除它需要的任何参数,并使用**kwds转发其余参数,最后把字典留空,等待链中的最终调用。
每一层都去掉它需要的关键字参数,这样最后的空dict就可以发送给一个根本不需要参数的方法(例如object.__init__不需要任何参数):
class Shape:
def __init__(self, shapename, **kwds):
self.shapename = shapename
super().__init__(**kwds)
class ColoredShape(Shape):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)
cs = ColoredShape(color='red', shapename='circle')
2)在研究了使调用者/被调用者参数模式匹配的策略之后,现在让我们看看如何确保目标方法存在。
上面的例子展示了最简单的情况。我们知道object有一个__init__方法,并且object总是MRO链中的最后一个类,所以保证了任何对super().__init__的调用都被以对object.__init__的调用结束。换句话说,我们保证super()调用的目标是存在的,不会因为AttributeError而失败。
对于object没有感兴趣的方法(例如draw()方法)的情况,我们需要编写一个保证在object之前被调用的根类。根类的职责只是吃掉方法调用,而不使用super()进行转发调用。
Root.draw还可以使用断言来使用防御性编程( defensive programming ),以确保它没有屏蔽链中稍后的其他draw()方法。如果子类错误地合并了一个具有draw()方法但不从Root继承的类,就会发生这种情况。
class Root:
def draw(self):
# the delegation chain stops here
assert not hasattr(super(), 'draw')
class Shape(Root):
def __init__(self, shapename, **kwds):
self.shapename = shapename
super().__init__(**kwds)
def draw(self):
print('Drawing. Setting shape to:', self.shapename)
super().draw()
class ColoredShape(Shape):
def __init__(self, color, **kwds):
self.color = color
super().__init__(**kwds)
def draw(self):
print('Drawing. Setting color to:', self.color)
super().draw()
cs = ColoredShape(color='blue', shapename='square')
cs.draw()
如果子类希望将其它类注入到MRO中,那么这些其它类也需要从根继承,这样,在Root.draw停止之前,调用draw()的任何路径都不能到达object。应该清楚地记录这一点,以便编写新的协作类的人员能够从Root中知道子类。这个限制与Python自己的要求没有太大不同,即所有新的异常都必须继承自BaseException。
3)上面显示的技术确保super()调用已知存在的方法,并且(方法的)签名是正确的;但是,我们仍然依赖于在每个步骤调用super(),这样委托链就不会中断。如果我们协作地设计类,这很容易实现——只需向链中的每个方法添加一个super()调用
上面列出的三种技术提供了设计可由子类组合或重新排序的协作类的方法。
How to Incorporate a Non-cooperative Class(如何合并非协作类)
有时,子类可能希望与不是为其设计的第三方类一起使用协作多重继承技术(可能其感兴趣的方法不使用super()或类不是从根类继承)。通过创建符合规则的适配器类( adapter class),可以很容易地纠正这种情况。
例如,下面的Moveable类不会调用super(),并且它有一个与object不兼容的__init__()签名。并且它不继承自Root:
class Moveable:
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
print('Drawing at position:', self.x, self.y)
如果我们想将这个类与我们合作设计的ColoredShape层次结构一起使用,我们需要用必需的super()调用创建一个适配器:
class MoveableAdapter(Root):
def __init__(self, x, y, **kwds):
self.movable = Moveable(x, y)
super().__init__(**kwds)
def draw(self):
self.movable.draw()
super().draw()
class MovableColoredShape(ColoredShape, MoveableAdapter):
pass
MovableColoredShape(color='red', shapename='triangle',
x=10, y=20).draw()
以上代码归一:
class object(object):
def __init__(self,**kwargs):
pass
class Root(object):
def draw(self):
print('self_Root:',self)
assert not hasattr(super(),'draw')
class Shape(Root):
def __init__(self,shapename, **kwargs):
self.shapename=shapename
super().__init__(**kwargs)
def draw(self):
print('--'*40)
print('self_Shape:',self)
print('super()_Shape:',super())
print('Drawing Setting shape to:',self.shapename)
super().draw()
class ColoredShape(Shape):
def __init__(self,color,**kwargs):
self.color=color
super().__init__(**kwargs)
def draw(self):
print('--'*40)
print('self_ColoredShape:',self)
print('super()_ColoredShape:',super())
print('Drawing Setting color to:',self.color)
super().draw()
class Moveable:
def __init__(self,x,y):
self.x=x
self.y=y
def draw(self):
print('self_Moveable:',self)
print("Drawing at position:",self.x,self.y)
class MoveableAdapter(Root):
def __init__(self,x,y,**kwargs):
self.moveable=Moveable(x,y)
super().__init__(**kwargs)
def draw(self):
print('--'*40)
print('self_MoveableAdapter:',self)
print('super()_MoveableAdapter:',super())
self.moveable.draw()
super().draw()
class MoveableColoredShape(ColoredShape, MoveableAdapter):
pass
test=MoveableColoredShape(color='red', shapename='triangle',x=10, y=20)
print(MoveableColoredShape.__mro__)
test.draw()
#运行结果:
(, , , , , , )
--------------------------------------------------------------------------------
self_ColoredShape: <__main__.MoveableColoredShape object at 0x037219D0>
super()_ColoredShape: , >
Drawing Setting color to: red
--------------------------------------------------------------------------------
self_Shape: <__main__.MoveableColoredShape object at 0x037219D0>
super()_Shape: , >
Drawing Setting shape to: triangle
--------------------------------------------------------------------------------
self_MoveableAdapter: <__main__.MoveableColoredShape object at 0x037219D0>
super()_MoveableAdapter: , >
self_Moveable: <__main__.Moveable object at 0x037219F0>
Drawing at position: 10 20
self_Root: <__main__.MoveableColoredShape object at 0x037219D0>
Complete Example – Just for Fun(完整的例子-只是为了好玩)
在Python 2.7和3.2中,collections模块同时具有Counter类和OrderedDict类。这些类很容易组成一个OrderedCounter:
from collections import Counter, OrderedDict
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first seen'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__,
OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
oc = OrderedCounter('abracadabra')
OrderedCounter.__mro__
Out[171]:
(__main__.OrderedCounter,
collections.Counter,
collections.OrderedDict,
dict,
object)
OrderedCounter.__bases__
Out[172]: (collections.Counter, collections.OrderedDict)
dir(oc)[-15:]
Out[174]:
['clear',
'copy',
'elements',
'fromkeys',
'get',
'items',
'keys',
'most_common',
'move_to_end',
'pop',
'popitem',
'setdefault',
'subtract',
'update',
'values']