调用super()?

2024-04-25 20:37:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在努力学习和理解如何在Python中使用super,我一直在关注《Python从新手到专家的旅程》一书,尽管我觉得我理解了在我自己的代码中执行super时遇到问题的概念。在

例如,这种方法适用于我:

class Employee:        
    def __init__(self, firstname, lastname, age, sex, dob):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age 
        self.sex = sex
        self.dob = dob
        self.all_staff.append(self)

class Hourly(Employee):
    def __init__(self, firstname, lastname, age, sex, dob, rate, hours):
        self.rate = rate
        self.hours = hours
        super().__init__(firstname, lastname, age, sex, dob)

    def __str__(self):
    return "{} {}\nAge: {}\nSex: {}\nDOB: {}\n".format(self.firstname, self.lastname, self.age, 
        self.sex, self.dob)

    def get_rate(self):
        print('The hourly rate of {} is {} '.format(self.firstname, self.rate))

hourlystaff1 = Hourly('Bob', 'Foo', '23', 'M', '12/1/1980', '$15', '30')

print(hourlystaff1)

print(hourlystaff1.get_rate())

返回以下值:

^{pr2}$

这就是我所期望的(我不确定为什么“None”也被返回,也许有人可以解释一下?)。在

然后我想用super来试试这个,但是使用的是**kwargs:

class Employee:
    def __init__(self, firstname='', lastname='', age='', dob='', **kwargs):
        super().__init__(**kwargs)
        self.firstname = firstname
        self.lastname = lastname
        self.age = age 
        self.dob = dob 

class Hourly(Employee):

    def __init__(self, rate=''):
        self.rate = rate
        super().__init__(**kwargs)

    def __str__(self):
        return "{} {}\nAge: {}\nSex: {}".format(self.firstname, self.lastname, self.age, 
            self.sex, self.dob, self.rate)

    def get_rate(self):
        print('The hourly rate of {} is {} '.format(self.firstname, self.rate))

bob = Hourly('Bob', 'Bar', '23', '12/1/2019')


bob.get_rate('$12')

返回此错误:

  File "staff_b.py", line 33, in <module>
    bob = Hourly('Bob', 'Bar', '23', '12/1/2019')
TypeError: __init__() takes from 1 to 2 positional arguments but 5 were given

在第二种方法中我做错了什么?如何正确使用**kwargs和super?在

编辑:

这是我一直关注的书中的一个例子的截图:

enter image description here

在我的第二个例子中,如何使用**kwargs和super有什么不同?在

这也是同一本书和同一章的综合案例研究。这对我很有用,我理解它的工作原理,但我似乎无法将其转化为我自己的工作。在

https://pastebin.com/NYGJfMik


Tags: selfformatagerateinitdefemployeefirstname
2条回答

你在这里遇到的问题并不是针对超级,而是针对夸克。如果我们扔掉你的大部分代码并删除了super,它看起来像这样:

class Hourly(Employee):

    def __init__(self, rate=''):
        self.rate = rate
        some_crazy_function(**kwargs)

hourlystaff1 = Hourly('Bob', 'Foo', '23', 'M', '12/1/1980', '$15', '30')

有两个明显的问题:__init__函数传递的参数比预期的多,并且在__init__函数的主体中引用了没有在任何地方定义的kwargs。而在这里,理解**kwargs(及其兄弟*args)就足以解决这里的问题super和{}是非常有用的。让我们先看看super为什么有用。让我们想象一下,我们用一些很好的辅助方法为子流程编写一些包装器(体系结构可能不是解决问题的最佳方法,但是只有看到具有继承性的动物也没有什么特别的帮助。多重继承是一种非常罕见的情况,因此很难找到不是动物、游戏实体或GUIwidgets的好例子):

^{pr2}$

在这里,我们正在进行继承,甚至不需要使用super-我们可以显式地使用超类的名称并获得我们想要的行为。我们可以在这里重写以使用super,但它不会改变行为。如果只从一个类继承,则不需要super,尽管它可以帮助您避免重复从中继承的类名。让我们加入我们的类层次结构,包括来自多个类的输入:

class AuthenticationCheckerProcess(Process):
    def __init__(self, exe, use_sha=True):
        self.check_if_authorized(exe, use_sha)
        Process.__init__(self, exe)

class DownloadAndCheck(DownloadExecutableBefore, AuthenticationCheckerProcess):
    def __init__(self, exe):
        DownloadExecutableBefore.__init__(exe)
        AuthenticationCheckerProcess.__init__(exe, use_sha=False)

如果我们遵循DownloadAndCheck的初始化,我们会看到Process.__init__被调用了两次,一次是通过{},一次是通过{}!所以我们要包装的过程也运行了两次,这不是我们想要的。在这个例子中,我们可以通过不在进程初始化中调用self.run()来轻松地解决这个问题,但在现实世界中,这并不总是像这里这样容易修复。在这种情况下,调用Process.__init__似乎是错误的。我们能解决这个问题吗?在

class DownloadAndCheck(DownloadExecutableBefore, AuthenticationCheckerProcess):
    def __init__(self, exe):
        super().__init__(exe, use_sha=False)
        # also replace the Process.__init__ cals in the other classes with super

super修复了这个问题,并且只调用Process.__init__一次。它还将处理函数的运行顺序,但这不是一个大问题。我们仍然有一个问题:use_sha=False将被传递给所有初始化器,但只有一个真正需要它。我们真的不能只将变量传递给需要它的函数(因为弄清楚这将是一场噩梦),但我们可以教导其他__init__忽略keywoard:

class Process:
    def __init__(self, exe, **kwargs):
        # accept arbitrary keywoards but ignore everything but exe
        # also put **kwargs in all other initializers
        self.exe = exe
        self.run()

class DownloadExecutableBeforeProcess(Process):
    def __init__(self, exe, **kwargs):
        self.download_exe(exe)
        # pass the keywoards into super so that other __init__s can use them
        Process.__init__(self, exe, **kwargs)

现在super().__init__(exe, use_sha=False)调用将成功,每个初始值设定项只接受它能理解的keywoard,并简单地将其他密钥向下传递。在

因此,如果您有多个继承并使用不同的(keywoard)参数super和kwargs可以解决您的问题。但是超级继承和多重继承是复杂的,特别是如果你有比这里更多的继承层。有时甚至没有定义调用函数的顺序(python应该抛出一个错误,参见explenation of change of MRO algorithm)。mixin甚至可能需要一个super().__init__()调用,尽管它们甚至不从任何类继承。总之,如果您使用多重继承,您的代码会变得非常复杂,因此,如果您不需要它,通常最好考虑其他方法来建模您的问题。在

这应该行得通

class Employee:
    def __init__(self, firstname='', lastname='', age='', dob=''):
        self.firstname = firstname
        self.lastname = lastname
        self.age = age 
        self.dob = dob 

那你就上儿童班了

^{pr2}$

现在,让我们举个例子

bob = Hourly2('Bob', 'Bar', '23', '12/1/2019')

我们可以检查属性

dir(bob)
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'age',
 'dob',
 'firstname',
 'get_rate',
 'lastname']

最后

bob.age
'23'

以及

bob.get_rate('$12')
My hourly rate is $12

相关问题 更多 >