我有一个课程定义了一个警报'然后一个定义了一个' httpalarm'。现在,httpalarm与警报共享许多相同的属性(实际上它们具有相同的属性,以及其他属性)。
我认为我应该能够做的是将这些属性定义为警报对象的一部分,然后当httpalarm创建为警报子节点时,它应该已经获得所有属性。但它并没有。至少,它似乎不会出现;
class alarm(object):
def __init__(self, severity, name, eventId):
self.severity = severity
self.name = name
self.eventId eventId
class httpalarm(alarm):
def __init__(self, **kwargs):
self._dict__.update(kwargs)
现在,当我创建一个警报对象时,我得到了我期望的结果:
a = alarm('Critical', 'Name', '0x800111')
vars(a)
->
{'severity': 'Critical', 'name': 'Name', 'eventId': '0x800111'}
这是预期的。但是当我创建一个httpalarm时:
b = httpalarm(test = 'Test')
vars(b)
->
{'test': 'Test'}
这是出乎意料的。我预计httpalarm b也会设置严重性,名称和eventId属性。
因此,我将警报类设置为使用类属性,而不是实例属性:
class alarm(object):
severity = 'Warning'
name = None
eventId = None
然后再次没有更改为httpalarm我再次测试;
c = httpalarm(test='Test')
vars(c)
->
{'test': 'Test'}
同样,vars没有显示所有其他属性。但是,它们被设置为;
print c.severity
'Warning'
打印继承的属性似乎确实有效。并且还使用dir(c)来显示对象确实返回所有属性
dir(c)
->
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eventId', 'name', 'severity', 'test']
我不确定这里发生了什么。我想创建一个可以继承的警报类,以免我不得不一直定义相同的属性。我想要得到的是:
d = httpalarm(severity = 'Critical', name = 'Name', eventId = '0x800111', test = 'test')
并让httpalarm实例d包含来自alarm的所有属性以及来自其自己的httpalarm类的属性。
编辑: 在Corley Brigman的提交之后,我已经按照预期的方式工作:
class httpalarm(alarm):
def __init__(self,
**kwargs):
super(httpalarm, self).__init__()
for k,v in self.__dict__.iteritems():
if type(v) == tuple:
setattr(self, k, v[0])
self.__dict__.update(kwargs)
答案 0 :(得分:1)
您需要自己调用alarm
的构造函数:
class httpalarm(alarm):
def __init__(self, **kwargs):
super(httpalarm, self).__init__()
# Your code here
为什么这里特别需要这个? Python的对象实现与C ++和Java等其他语言有很大不同;它必须对如何创建对象做很多事情。
C ++对象基本上是一个内存结构,附加了一些函数。 Python对象更像是一个附加了一些函数的字典。这是怎么发生的?
在这两种环境中,您可以想象创建(new
,但通常在C ++中幕后; __new__
作为Python中类的函数)和初始化(C ++中的构造函数,和Python中的__init__
)作为两个单独的部分。
在C ++中,由于内存结构对于对象的操作至关重要,因此当您调用new
(在高级别)时,系统会为使用正确大小声明的每个属性分配空间。这是静态的 - 您无法动态创建新属性,只能修改声明的属性。请注意,这还要求在您进行子类化时,您还要确切地声明结构的外观 - 因此编译器可以构建正确的内存映像。
然后,您调用构造函数,它实际上根据输入和程序员想要的任何内容初始化值。在许多情况下,如果您对初始化的默认值感到满意,您实际上可以将构造函数保留在C ++中,或者声明它但将其保留为空。
在Python中,由于对象模型更像是一个可变的dict,基类行为('创建一个字典并附加到对象')就是创建该字典。这通常足以满足大多数用法,因此在Python中,您几乎看不到写入__new__
函数(*)。
相反,大多数人的默认设置是初始化__init__
函数中的变量。重要的是在创建对象之后将其称为。然后,您可以将所需的任何变量设置为您想要的任何数据(无论何种类型)。
关键是在两种情况下,在调用初始化程序之前创建对象。在C ++中,创建创建数据结构。在Python中,对象创建(通常)只是创建一个字典 - 在alarm.__init__
和httpalarm.__init__
的条目中,两个对象看起来就像一个空字典。 init必须创建必要的属性。在C ++中调用基类构造函数也并不罕见,但它在Python中更为常见,因为基类构造函数还创建了子类使用的公共属性。
你可以使用插槽 - 它们更接近C ++的工作方式,但不完全正确。
对于vars
- vars
仅显示本地属性 - 您在__init__
或其他位置设置的属性。当您将它们声明为类变量时,它们将显示在dir
中,因为它显示了类属性和本地设置的对象属性,但不显示vars
。来自docstring:
vars([object]) -> dictionary
Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.
类变量位于object.__class__.__dict__
而不是object.__dict__
。
如果您真的想要这种行为,您实际上可以使用元类,并覆盖__new__
函数来创建一些默认属性。它并不常见,但它更像是C ++(据我所知......我也没有做过那么多)。但它有时会完成(通常称为“元编程”或类似的东西),特别是如果你想要动态属性,因为属性(必然)是类属性,并且不能动态设置宾语;相反,您使用类工厂,它动态添加所需的属性。