我正在尝试学习和理解如何在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())
返回以下内容:
Bob Foo
Age: 23
Sex: M
DOB: 12/1/1980
The hourly rate of Bob is $15
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?
编辑:
这是我一直关注的书中的一个例子的截图:
我在第二个例子中使用** kwargs和super之间的区别是什么?
这也是同一本书和章节的综合案例研究。这适用于我,我理解它是如何工作的,但我似乎无法将其翻译成我自己的工作。
答案 0 :(得分:4)
你在这里遇到的问题并不是特定于超级但对kwargs更具体。如果我们抛出你的大部分代码并移除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
)足以解决这里的问题,而且**kwargs
非常有用。让我们首先看一下super
有用的原因。让我们想象一下,我们用一些不错的帮助方法在子进程周围写一些包装器(这个架构可能不是最适合这个问题的,但是只看到有继承的动物也没有超级帮助。多重继承是一种非常罕见的情况,所以它&# 39;很难想出不是动物,GameEntities或GUIwidgets的好例子:
class Process:
def __init__(self, exe):
self.exe = exe
self.run()
class DownloadExecutableBeforeProcess(Process):
def __init__(self, exe):
self.download_exe(exe)
Process.__init__(self, exe)
这里我们正在进行继承,我们甚至不需要使用super - 我们可以只显式使用超类的名称并拥有我们想要的行为。我们可以在这里重写使用super
但不会改变行为。如果您只从一个班级继承,则不必严格需要super
,尽管它可以帮助您不重复您继承的班级名称。让我们加入我们的课程hirarchy并包括来自多个班级的入学:
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__
被调用两次,一次通过DownloadExecutableBefore.__init__
,一次通过AuthenticationCheckerProcess.__init__
!所以我们要包装的过程也会运行两次,这不是我们想要的。在这个例子中,我们可以通过不在进程的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)参数super和kwargs可以解决你的问题。但是超级和多重继承很复杂,特别是如果你有比这里更多的继承层。有时函数应该是calles的顺序甚至没有定义(然后python应该抛出一个错误,参见例如explenation of change of MRO algorithm)。 Mixins甚至可能需要super().__init__()
电话,尽管他们甚至不会从任何课程继承。总而言之,如果你使用多重继承,你会在代码中获得很多复杂性,所以如果你真的不需要它,那么通常更好地考虑其他方法来模拟你的问题。
答案 1 :(得分:-1)
这应该有效
class Employee:
def __init__(self, firstname='', lastname='', age='', dob=''):
self.firstname = firstname
self.lastname = lastname
self.age = age
self.dob = dob
然后你有孩子班
class Hourly2(Employee):
def __init__(self,*args, **kwargs):
super(Hourly2,self).__init__(*args, **kwargs)
def get_rate(self,rate=None):
self.data=rate
print ('My hourly rate is {}'.format(self.data))
现在,让我们创建实例
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