为什么在这种情况下对 super() 的调用不使用 __getattr__?
Why is the call to super() not using __getattr__ in this case?
所以我有一个组合,为什么我的包装器 class 包含一个 Pandas DataFrame。因为我想给它添加一些行为,所以我设置了以下内容:
class DataFrameWrapper(dict):
def __init__(self, dataframe, *args, **kwargs):
self.df = dataframe
self.pandas_callables = [method_name for method_name in dir(self.df)
if callable(getattr(self.df, method_name))]
super(DataFrameWrapper, self).__init__()
def __getitem__(self, item):
return self.df[item]
def __getattr__(self, item):
if item in self.pandas_callables:
# this is a dataframe method call - forward to the dataframe & return that
return object.__getattribute__(self.df, item)
else:
try: # try to return our own attribute, if any
return object.__getattribute__(self, item)
except AttributeError as ex:
# likely a panda attribute. IF not, then it's a genuine attribute
# error, so we don't catch it and let it raise another exception
return object.__getattribute__(self.df, item)
那我说
class Foo(DataFrameWrapper):
def __init__(self, dataframe, *args, **kwargs):
super().__init__(dataframe, *args, **kwargs)
class Bar(Foo)
""" concrete implementation class """
class Baz(Foo)
""" concrete implementation class """
这样我们可以做到:
bar = Bar(df)
bar.to_json()
bar.some_custom_method()
col = bar["column_name"]
现在,如果我说:
json = bar.to_json()
这很好用。但是,我想在 Foo 中添加额外的处理,所以我想这样做:
class Foo(DataFrameWrapper):
def to_json(*args, **kwrags)
# do additional stuff
super().to_json(*args, **kwrags)
但是在那种情况下,__getattr__()
永远不会在包装器中调用,我只是得到
AttributeError: 'super' object has no attribute 'to_json'
为什么?
编辑:
如果我做一些愚蠢的事情,像这样:
class DataFrameWrapper(dict):
### previous code unchanged ###
def __getattribute__(self, item):
if item == "to_json":
return object.__getattr__(self, item)
return object.__getattribute__(self, item)
然后调用 to_json() 就可以了。 According to the docs,我希望我刚刚所做的 hack 是 应该 发生的事情。
好的,根据@martineau 上面的评论尝试尝试回答。
所以是的,super()
其实并不是简单的直接调用父类的方法。因为 Python 支持多重继承,所以对 super().foo()
的调用必须做的不仅仅是调用(第一个)父 class' __getattribute__
。它必须建立某种机制来确保python 的多重继承实现是连贯的。
也就是说,如果我直接调用父方法(old-school 根据 Python super()'s guide),那么它会按我最初的预期工作:
class Foo(DataFrameWrapper):
def to_json(self, *args, **kwargs):
# do stuff
return DataFrameWrapper.__getattr__(self, "to_json")(*args, **kwargs)
在我获得预期功能的意义上“解决”了问题。
现在了解为什么 super() 不起作用:
super
有一个 __getattribute__
方法,但 没有 一个 __getattr__
方法。因此我猜测发生的事情可能是这样的:
super().__getattribute__("to_json")
被称为
super
通过 MRO 尝试找到属性
- 根据下面的评论,它会在每个 classe 的字典中查找它正在寻找的属性。出于效率原因,我猜测它实际上并没有调用每个 classe 的 getattribute 方法。
- 有关示例的详细信息,请参阅下面的评论。也许最终我会把它组织成答案。
所以我有一个组合,为什么我的包装器 class 包含一个 Pandas DataFrame。因为我想给它添加一些行为,所以我设置了以下内容:
class DataFrameWrapper(dict):
def __init__(self, dataframe, *args, **kwargs):
self.df = dataframe
self.pandas_callables = [method_name for method_name in dir(self.df)
if callable(getattr(self.df, method_name))]
super(DataFrameWrapper, self).__init__()
def __getitem__(self, item):
return self.df[item]
def __getattr__(self, item):
if item in self.pandas_callables:
# this is a dataframe method call - forward to the dataframe & return that
return object.__getattribute__(self.df, item)
else:
try: # try to return our own attribute, if any
return object.__getattribute__(self, item)
except AttributeError as ex:
# likely a panda attribute. IF not, then it's a genuine attribute
# error, so we don't catch it and let it raise another exception
return object.__getattribute__(self.df, item)
那我说
class Foo(DataFrameWrapper):
def __init__(self, dataframe, *args, **kwargs):
super().__init__(dataframe, *args, **kwargs)
class Bar(Foo)
""" concrete implementation class """
class Baz(Foo)
""" concrete implementation class """
这样我们可以做到:
bar = Bar(df)
bar.to_json()
bar.some_custom_method()
col = bar["column_name"]
现在,如果我说:
json = bar.to_json()
这很好用。但是,我想在 Foo 中添加额外的处理,所以我想这样做:
class Foo(DataFrameWrapper):
def to_json(*args, **kwrags)
# do additional stuff
super().to_json(*args, **kwrags)
但是在那种情况下,__getattr__()
永远不会在包装器中调用,我只是得到
AttributeError: 'super' object has no attribute 'to_json'
为什么?
编辑:
如果我做一些愚蠢的事情,像这样:
class DataFrameWrapper(dict):
### previous code unchanged ###
def __getattribute__(self, item):
if item == "to_json":
return object.__getattr__(self, item)
return object.__getattribute__(self, item)
然后调用 to_json() 就可以了。 According to the docs,我希望我刚刚所做的 hack 是 应该 发生的事情。
好的,根据@martineau 上面的评论尝试尝试回答。
所以是的,super()
其实并不是简单的直接调用父类的方法。因为 Python 支持多重继承,所以对 super().foo()
的调用必须做的不仅仅是调用(第一个)父 class' __getattribute__
。它必须建立某种机制来确保python 的多重继承实现是连贯的。
也就是说,如果我直接调用父方法(old-school 根据 Python super()'s guide),那么它会按我最初的预期工作:
class Foo(DataFrameWrapper):
def to_json(self, *args, **kwargs):
# do stuff
return DataFrameWrapper.__getattr__(self, "to_json")(*args, **kwargs)
在我获得预期功能的意义上“解决”了问题。
现在了解为什么 super() 不起作用:
super
有一个 __getattribute__
方法,但 没有 一个 __getattr__
方法。因此我猜测发生的事情可能是这样的:
super().__getattribute__("to_json")
被称为super
通过 MRO 尝试找到属性- 根据下面的评论,它会在每个 classe 的字典中查找它正在寻找的属性。出于效率原因,我猜测它实际上并没有调用每个 classe 的 getattribute 方法。
- 有关示例的详细信息,请参阅下面的评论。也许最终我会把它组织成答案。