简易用法
1,通常子类中使用super方法可以调用父类中的方法。
class A(object):
def __init__(self):
print("I'am father")
class B(A):
def __init__(self):
super().__init__()
print("I'am child")
if __name__ == "__main__":
b = B()
result:
I'am father
I'am child
需要注意的是,super()
是python3允许的写法,python2需要写成如super(B,self)
的形式。下面举例时不加说明自己区分吧。
2,在继承父类的子类中往往需要在子类的构造方法里先调用父类的构造方法(为了reuse父类code嘛!)
class A(object):
def __init__(self):
self.name = "kate"
class B(A):
def __init__(self):
super(B,self).__init__()
self.age = 27
if __name__ == "__main__":
b = B()
print(b.name)
print(b.age)
result:
kate
27
在单继承时有以下两种形式去调用父类的构造方法:
class Child(SuperClass):
def __init__(self):
super(Child, self).__init__()
和
class Child(SuperClass):
def __init__(self):
SuperClass.__init__(self)
这时两者区别不大,不过使用super()可以避免将父类名硬编码地写在代码里,不过这好像也没啥大卵用。
在多继承时使用super会按照MRO解析规则决定执行那个被继承的父类方法:
class A(object):
def __init__(self):
print("I'am father A")
class B(object):
def __init__(self):
print("I'am father B")
class C(A,B):
def __init__(self):
super(C,self).__init__()
print("I'am child!")
if __name__ == "__main__":
c = C()
result:
I'am father A
I'am child!
请比较下面的写法(虽然没有报错,但是同时执行了两个父类的同名方法,可能会造成严重后果和混乱,所以坚决不能这样写,而应该让它严格遵循MRO解析规则,毕竟无规矩不成方圆嘛!):
class A(object):
def __init__(self):
print("I'am father A")
class B(object):
def __init__(self):
print("I'am father B")
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self)
print("I'am child!")
if __name__ == "__main__":
c = C()
result:
I'am father A
I'am father B
I'am child!
综上:推荐使用super方法调用父类的方法。(但防不住有人在单继承上使用这种硬编码格式啊,所以看到理解即可)
复杂模式
python2
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type) -> unbound super object
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
# (copied from class doc)
python3
super() -> same as super(__class__, <first argument>)
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
def meth(self, arg):
super().meth(arg) # 相当于super(C,self) ,即super(type, obj),用来调
# 用超类(父类)的实例方法
This works for class methods too:
class C(B):
@classmethod
def cmeth(cls, arg):
super().cmeth(arg) #相当于super(C, cls) ,即super(type,type2),用来调
# 用超类(父类)的类方法
# (copied from class doc)
一个例子的解读:
class A(object):
def func(self):
print('A')
class B(A):
def func(self):
super().func() # 相当于super(B, self) ,即super(B, d)
print('B')
class C(A):
def func(self):
super().func() # 相当于super(C, self) ,即super(C, d)
print('C')
class D(B,C):
def func(self):
super().func() # 相当于super(D, self) ,即super(D, d)
print('D')
d = D()
d.func()
print(D.mro())
result:
A
C
B
D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
Process finished with exit code 0
看出来了吗,它们传递的self对象都是d,按照mro的解析顺序解析就好了。
更多其它用法参考test_super.py:
"""Unit tests for zero-argument super() & related machinery."""
import unittest
class A:
def f(self):
return 'A'
@classmethod
def cm(cls):
return (cls, 'A')
class B(A):
def f(self):
return super().f() + 'B'
@classmethod
def cm(cls):
return (cls, super().cm(), 'B')
class C(A):
def f(self):
return super().f() + 'C'
@classmethod
def cm(cls):
return (cls, super().cm(), 'C')
class D(C, B):
def f(self):
return super().f() + 'D'
def cm(cls):
return (cls, super().cm(), 'D')
class E(D):
pass
class F(E):
f = E.f
class G(A):
pass
class TestSuper(unittest.TestCase):
def tearDown(self):
# This fixes the damage that test_various___class___pathologies does.
nonlocal __class__
__class__ = TestSuper
def test_basics_working(self):
self.assertEqual(D().f(), 'ABCD')
def test_class_getattr_working(self):
self.assertEqual(D.f(D()), 'ABCD')
def test_subclass_no_override_working(self):
self.assertEqual(E().f(), 'ABCD')
self.assertEqual(E.f(E()), 'ABCD')
def test_unbound_method_transfer_working(self):
self.assertEqual(F().f(), 'ABCD')
self.assertEqual(F.f(F()), 'ABCD')
def test_class_methods_still_working(self):
self.assertEqual(A.cm(), (A, 'A'))
self.assertEqual(A().cm(), (A, 'A'))
self.assertEqual(G.cm(), (G, 'A'))
self.assertEqual(G().cm(), (G, 'A'))
def test_super_in_class_methods_working(self):
d = D()
self.assertEqual(d.cm(), (d, (D, (D, (D, 'A'), 'B'), 'C'), 'D'))
e = E()
self.assertEqual(e.cm(), (e, (E, (E, (E, 'A'), 'B'), 'C'), 'D'))
def test_super_with_closure(self):
# Issue4360: super() did not work in a function that
# contains a closure
class E(A):
def f(self):
def nested():
self
return super().f() + 'E'
self.assertEqual(E().f(), 'AE')
def test_various___class___pathologies(self):
# See issue #12370
class X(A):
def f(self):
return super().f()
__class__ = 413
x = X()
self.assertEqual(x.f(), 'A')
self.assertEqual(x.__class__, 413)
class X:
x = __class__
def f():
__class__
self.assertIs(X.x, type(self))
with self.assertRaises(NameError) as e:
exec("""class X:
__class__
def f():
__class__""", globals(), {})
self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
class X:
global __class__
__class__ = 42
def f():
__class__
self.assertEqual(globals()["__class__"], 42)
del globals()["__class__"]
self.assertNotIn("__class__", X.__dict__)
class X:
nonlocal __class__
__class__ = 42
def f():
__class__
self.assertEqual(__class__, 42)
def test___class___instancemethod(self):
# See issue #14857
class X:
def f(self):
return __class__
self.assertIs(X().f(), X)
def test___class___classmethod(self):
# See issue #14857
class X:
@classmethod
def f(cls):
return __class__
self.assertIs(X.f(), X)
def test___class___staticmethod(self):
# See issue #14857
class X:
@staticmethod
def f():
return __class__
self.assertIs(X.f(), X)
def test___class___new(self):
# See issue #23722
# Ensure zero-arg super() works as soon as type.__new__() is completed
test_class = None
class Meta(type):
def __new__(cls, name, bases, namespace):
nonlocal test_class
self = super().__new__(cls, name, bases, namespace)
test_class = self.f()
return self
class A(metaclass=Meta):
@staticmethod
def f():
return __class__
self.assertIs(test_class, A)
def test___class___delayed(self):
# See issue #23722
test_namespace = None
class Meta(type):
def __new__(cls, name, bases, namespace):
nonlocal test_namespace
test_namespace = namespace
return None
class A(metaclass=Meta):
@staticmethod
def f():
return __class__
self.assertIs(A, None)
B = type("B", (), test_namespace)
self.assertIs(B.f(), B)
def test___class___mro(self):
# See issue #23722
test_class = None
class Meta(type):
def mro(self):
# self.f() doesn't work yet...
self.__dict__["f"]()
return super().mro()
class A(metaclass=Meta):
def f():
nonlocal test_class
test_class = __class__
self.assertIs(test_class, A)
def test___classcell___expected_behaviour(self):
# See issue #23722
class Meta(type):
def __new__(cls, name, bases, namespace):
nonlocal namespace_snapshot
namespace_snapshot = namespace.copy()
return super().__new__(cls, name, bases, namespace)
# __classcell__ is injected into the class namespace by the compiler
# when at least one method needs it, and should be omitted otherwise
namespace_snapshot = None
class WithoutClassRef(metaclass=Meta):
pass
self.assertNotIn("__classcell__", namespace_snapshot)
# With zero-arg super() or an explicit __class__ reference,
# __classcell__ is the exact cell reference to be populated by
# type.__new__
namespace_snapshot = None
class WithClassRef(metaclass=Meta):
def f(self):
return __class__
class_cell = namespace_snapshot["__classcell__"]
method_closure = WithClassRef.f.__closure__
self.assertEqual(len(method_closure), 1)
self.assertIs(class_cell, method_closure[0])
# Ensure the cell reference *doesn't* get turned into an attribute
with self.assertRaises(AttributeError):
WithClassRef.__classcell__
def test___classcell___missing(self):
# See issue #23722
# Some metaclasses may not pass the original namespace to type.__new__
# We test that case here by forcibly deleting __classcell__
class Meta(type):
def __new__(cls, name, bases, namespace):
namespace.pop('__classcell__', None)
return super().__new__(cls, name, bases, namespace)
# The default case should continue to work without any errors
class WithoutClassRef(metaclass=Meta):
pass
# With zero-arg super() or an explicit __class__ reference, we expect
# __build_class__ to raise a RuntimeError complaining that
# __class__ was not set, and asking if __classcell__ was propagated
# to type.__new__.
expected_error = '__class__ not set.*__classcell__ propagated'
with self.assertRaisesRegex(RuntimeError, expected_error):
class WithClassRef(metaclass=Meta):
def f(self):
return __class__
def test___classcell___overwrite(self):
# See issue #23722
# Overwriting __classcell__ with nonsense is explicitly prohibited
class Meta(type):
def __new__(cls, name, bases, namespace, cell):
namespace['__classcell__'] = cell
return super().__new__(cls, name, bases, namespace)
for bad_cell in (None, 0, "", object()):
with self.subTest(bad_cell=bad_cell):
with self.assertRaises(TypeError):
class A(metaclass=Meta, cell=bad_cell):
pass
def test___classcell___wrong_cell(self):
# See issue #23722
# Pointing the cell reference at the wrong class is also prohibited
class Meta(type):
def __new__(cls, name, bases, namespace):
cls = super().__new__(cls, name, bases, namespace)
B = type("B", (), namespace)
return cls
with self.assertRaises(TypeError):
class A(metaclass=Meta):
def f(self):
return __class__
def test_obscure_super_errors(self):
def f():
super()
self.assertRaises(RuntimeError, f)
def f(x):
del x
super()
self.assertRaises(RuntimeError, f, None)
class X:
def f(x):
nonlocal __class__
del __class__
super()
self.assertRaises(RuntimeError, X().f)
def test_cell_as_self(self):
class X:
def meth(self):
super()
def f():
k = X()
def g():
return k
return g
c = f().__closure__[0]
self.assertRaises(TypeError, X.meth, c)
def test_super_init_leaks(self):
# Issue #26718: super.__init__ leaked memory if called multiple times.
# This will be caught by regrtest.py -R if this leak.
# NOTE: Despite the use in the test a direct call of super.__init__
# is not endorsed.
sp = super(float, 1.0)
for i in range(1000):
super.__init__(sp, int, i)
if __name__ == "__main__":
unittest.main()