python面试的100题(15)

41.super函数的具体用法和场景

为了调用父类(超类)的一个方法,可以使用 super() 函数,比如:

class A:
    def spam(self):
        print(‘A.spam‘)

class B(A):
    def spam(self):
        print(‘B.spam‘)
        super().spam()  # Call parent spam()

super() 函数的一个常见用法是在 __init__() 方法中确保父类被正确的初始化了:

class A:
    def __init__(self):
        self.x = 0

class B(A):
    def __init__(self):
        super().__init__()
        self.y = 1

super() 的另外一个常见用法出现在覆盖Python特殊方法的代码中,比如:

class Proxy:
    def __init__(self, obj):
        self._obj = obj

    # Delegate attribute lookup to internal obj
    def __getattr__(self, name):
        return getattr(self._obj, name)

    # Delegate attribute assignment
    def __setattr__(self, name, value):
        if name.startswith(‘_‘):
            super().__setattr__(name, value) # Call original __setattr__
        else:
            setattr(self._obj, name, value)

在上面代码中,__setattr__() 的实现包含一个名字检查。 如果某个属性名以下划线(_)开头,就通过 super() 调用原始的 __setattr__() , 否则的话就委派给内部的代理对象 self._obj 去处理。 这看上去有点意思,因为就算没有显式的指明某个类的父类, super() 仍然可以有效的工作。

讨论

实际上,大家对于在Python中如何正确使用 super() 函数普遍知之甚少。 你有时候会看到像下面这样直接调用父类的一个方法:

class Base:
    def __init__(self):
        print(‘Base.__init__‘)

class A(Base):
    def __init__(self):
        Base.__init__(self)
        print(‘A.__init__‘)

尽管对于大部分代码而言这么做没什么问题,但是在更复杂的涉及到多继承的代码中就有可能导致很奇怪的问题发生。 比如,考虑如下的情况:

class Base:
    def __init__(self):
        print(‘Base.__init__‘)

class A(Base):
    def __init__(self):
        Base.__init__(self)
        print(‘A.__init__‘)

class B(Base):
    def __init__(self):
        Base.__init__(self)
        print(‘B.__init__‘)

class C(A,B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print(‘C.__init__‘)

如果你运行这段代码就会发现 Base.__init__() 被调用两次,如下所示:

>>> c = C()
Base.__init__
A.__init__
Base.__init__
B.__init__
C.__init__
>>>

可能两次调用 Base.__init__() 没什么坏处,但有时候却不是。 另一方面,假设你在代码中换成使用 super() ,结果就很完美了:

class Base:
    def __init__(self):
        print(‘Base.__init__‘)

class A(Base):
    def __init__(self):
        super().__init__()
        print(‘A.__init__‘)

class B(Base):
    def __init__(self):
        super().__init__()
        print(‘B.__init__‘)

class C(A,B):
    def __init__(self):
        super().__init__()  # Only one call to super() here
        print(‘C.__init__‘)

运行这个新版本后,你会发现每个 __init__() 方法只会被调用一次了:

>>> c = C()
Base.__init__
B.__init__
A.__init__
C.__init__
>>>

为了弄清它的原理,我们需要花点时间解释下Python是如何实现继承的。 对于你定义的每一个类,Python会计算出一个所谓的方法解析顺序(MRO)列表。 这个MRO列表就是一个简单的所有基类的线性顺序表。例如:

>>> C.__mro__
(<class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘__main__.B‘>,
<class ‘__main__.Base‘>, <class ‘object‘>)
>>>

为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

而这个MRO列表的构造是通过一个C3线性化算法来实现的。 我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父类

老实说,你所要知道的就是MRO列表中的类顺序会让你定义的任意类层级关系变得有意义。

当你使用 super() 函数时,Python会在MRO列表上继续搜索下一个类。 只要每个重定义的方法统一使用 super() 并只调用它一次, 那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次。 这也是为什么在第二个例子中你不会调用两次 Base.__init__() 的原因。

super() 有个令人吃惊的地方是它并不一定去查找某个类在MRO中下一个直接父类, 你甚至可以在一个没有直接父类的类中使用它。例如,考虑如下这个类:

class A:
    def spam(self):
        print(‘A.spam‘)
        super().spam()

如果你试着直接使用这个类就会出错:

>>> a = A()
>>> a.spam()
A.spam
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 4, in spam
AttributeError: ‘super‘ object has no attribute ‘spam‘
>>>

但是,如果你使用多继承的话看看会发生什么:

>>> class B:
...     def spam(self):
...         print(‘B.spam‘)
...
>>> class C(A,B):
...     pass
...
>>> c = C()
>>> c.spam()
A.spam
B.spam
>>>

你可以看到在类A中使用 super().spam() 实际上调用的是跟类A毫无关系的类B中的 spam() 方法。 这个用类C的MRO列表就可以完全解释清楚了:

>>> C.__mro__
(<class ‘__main__.C‘>, <class ‘__main__.A‘>, <class ‘__main__.B‘>,
<class ‘object‘>)
>>>

在定义混入类的时候这样使用 super() 是很普遍的。可以参考8.13和8.18小节。

然而,由于 super() 可能会调用不是你想要的方法,你应该遵循一些通用原则。 首先,确保在继承体系中所有相同名字的方法拥有可兼容的参数签名(比如相同的参数个数和参数名称)。 这样可以确保 super() 调用一个非直接父类方法时不会出错。 其次,最好确保最顶层的类提供了这个方法的实现,这样的话在MRO上面的查找链肯定可以找到某个确定的方法。

在Python社区中对于 super() 的使用有时候会引来一些争议。 尽管如此,如果一切顺利的话,你应该在你最新代码中使用它。 Raymond Hettinger为此写了一篇非常好的文章 “Python’s super() Considered Super!” , 通过大量的例子向我们解释了为什么 super() 是极好的。

参考地址:https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p07_calling_method_on_parent_class.html

原文地址:https://www.cnblogs.com/Fiona-Y/p/10572816.html

时间: 2024-10-05 05:04:48

python面试的100题(15)的相关文章

python面试的100题(21)

正则表达式 94.请写出一段代码用正则匹配出ip? ip地址的生成规则. IP地址,是由32位数字二进制转为四个十进制的字符串组成. 怎么转化?下面讲解: 二进制:11111111111111111111111111111111 分为四部分:11111111.11111111.11111111.11111111 转化:2^7+2^6+2^5+2^4+2^3+2^2+2^1+2^0=255 转为十进制范围:0~255.0~255.0~255.0~255 这就是IP地址的范围. 根据这个生成IP的规

python面试的100题(9)

17.python如何实现单例模式?请写出两种实现方式? 第一种方法:使用装饰器 def singleton(cls): instances = {} def wrapper(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return wrapper @singleton class Foo(object): pass foo1 = Fo

python面试的100题(18)

函数 52.python常见的列表推导式? 列表推导式书写形式: [表达式 for 变量 in 列表] 或者 [表达式 for 变量 in 列表 if 条件] 参考地址:https://www.cnblogs.com/yupeng/p/3428556.html 53.简述read.readline.readlines的区别? (1) read(size):按字节从头读到最后,返回的是一个字符串类型,其中参数size是表示读取的字节数,size的默认是读取全部. (2) readline() :每

python面试的100题(7)

8.将字符串 "k:1 |k1:2|k2:3|k3:4",处理成字典 {k:1,k1:2,...} str1 = "k:1|k1:2|k2:3|k3:4" def str2dict(str1): dict1 = {} for iterms in str1.split('|'): key,value = iterms.split(':') dict1[key] = value return dict1 #字典推导式 d = {k:int(v) for t in str

LeetCode面试常见100题( TOP 100 Liked Questions)

LeetCode面试常见100题( TOP 100 Liked Questions) 置顶 2018年07月16日 11:25:22 lanyu_01 阅读数 9704更多 分类专栏: 面试编程题真题合集 常见算法问题 LeetCode试题 LeetCode常见试题 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/lanyu_01/article/details/81062232 这篇文章

python面试的315题

copy自武sir的博客,如有疑问请联系武sir 第一部分 Python基础篇(80题) 为什么学习Python? 通过什么途径学习的Python? Python和Java.PHP.C.C#.C++等其他语言的对比? 简述解释型和编译型编程语言? Python解释器种类以及特点? 位和字节的关系? b.B.KB.MB.GB 的关系? 请至少列举5个 PEP8 规范(越多越好). 通过代码实现如下转换: 二进制转换成十进制:v = “0b1111011”?十进制转换成二进制:v = 18? 八进制

python练习集100题(1-20)

题目1:有1.2.3.4四个数,能组成多少个互不相同且无重复的三位数?都是多少? list_num=[1,2,3,4] all_num=[] for i in list_num:     for j in list_num:         for k in list_num :             if (i!=j) and (i!=k) and (j!=k):                 num=i*100+j*10+k                 all_num.append(

python自测100题,来看看你和月薪3万的差距(含答案)

-------------人生苦短,必须性感! 1.为什么学习Python? 人生苦短?人间不值得?想想自己的初心吧! 2.通过什么途径学习的Python? 官网.网上视频.学习网站.论坛.大牛的辅导 3.Python和Java.PHP.C.C#.C++等其他语言的对比? (1).python代码,简介,明确,优雅,简单易懂(2).开发效率高(3).可扩展性强 4.简述解释型和编译型编程语言? 解释型:在执行程序时,计算机才一条一条的将代码解释成机器语言给计算机来执行编译型:是把源程序的每一条语

python练习集100题(21-40)

题目21:两个乒乓球队进行比赛,各出3人.甲队为a,b,c三人,乙队为x,y,z三人.以抽签决定比赛名单.有人向队员打听比赛的名单.a说他不和x比,c说他不和x.z比,请编程找出三队比赛名单. first_list=['x','y','z'] for i in first_list:        #i是a的对手,j是b的对手,k是c的对手     for j in first_list:         if(j!=i):                        for k in fir