如何重定向Python中包含的类的所有方法?

如何重定向Python中包含的类的所有方法?,python,inheritance,pandas,composition,Python,Inheritance,Pandas,Composition,如何实现组合模式?我有一个类容器,其中包含一个属性对象。我想通过调用my\u Container.some\u Contained\u method()从Container重定向/允许访问Contained类的所有方法。我是否以正确的方式做正确的事情 我用的是: class Container: def __init__(self): self.contained = Contained() def __getattr__(self, item): if

如何实现组合模式?我有一个类
容器
,其中包含一个属性对象
。我想通过调用
my\u Container.some\u Contained\u method()
Container
重定向/允许访问
Contained
类的所有方法。我是否以正确的方式做正确的事情

我用的是:

class Container:
   def __init__(self):
       self.contained = Contained()
   def __getattr__(self, item):
       if item in self.__dict__: # some overridden
           return self.__dict__[item] 
       else:
           return self.contained.__getattr__(item) # redirection
背景:

我正在尝试构建一个类(
Indicator
),它添加到现有类(
pandas.DataFrame
)的功能中<代码>指示器将具有数据帧的所有方法。我可以使用继承,但我遵循“偏好组合而非继承”的建议(例如,请参见:)中的答案。不继承的一个原因是基类不可序列化,我需要序列化

我已经找到了,但我不确定它是否适合我的需要。

注意事项:

  • 数据帧有很多属性。如果
    DataFrame
    属性是一个数字,您可能只想返回该数字。但是如果
    DataFrame
    属性是
    DataFrame
    ,则可能需要返回一个
    容器。如果
    DataFrame
    属性是
    Series
    或描述符,我们应该怎么做?要正确地实现
    容器。\uuu getattr\uuuu
    ,您真的需要 必须为每个属性编写单元测试
  • \uuu getitem\uu
    也需要进行单元测试
  • 您还必须定义和单元测试
    \uuuuuu setattr\uuuuuu
    \uuuuuuu setitem\uuuuuuuuu
    \uuuuuu iter\uuuuuuuuuuuuu
    ,等等
  • pickle是序列化的一种形式,因此如果
    数据帧
    是可pickle的,我不确定
    容器
    对序列化有什么帮助
一些评论:

  • \uuuu getattr\uuuu
    仅当属性不在
    self.\uuuu dict\uuuu
    中时才会调用。因此,您不需要在您的
    \uu getattr\uuuu
    中使用self中的

  • self.contained.\uuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuuuu(项目)
    调用
    self.contained
    \uuuu getattr\uuuu
    方法。这通常不是你想要的 因为它绕过了整个Python属性查找 机制。例如,它忽略了属性 可以是独立的,也可以是其中一个的
    自包含的基础。\uuuuu类\uuuuu
    或如果
    指的是 描述符。而是使用
    getattr(自包含项)


下面是一些随机代码,用于测试--(至少表面上--
容器
是否正确地委托到
数据帧
并返回
容器

df = pandas.DataFrame(
    [(1, 2), (1, 3), (1, 4), (2, 1),(2,2,)], columns=['col1', 'col2'])
df = Container(df)
df['col1'][3] = 0
print(df)
#    col1  col2
# 0     1     2
# 1     1     3
# 2     1     4
# 3     2     1
# 4     2     2
gp = df.groupby('col1').aggregate(np.count_nonzero)
print(gp)
#       col2
# col1      
# 1        3
# 2        2
print(type(gp))
# <class '__main__.Container'>

print(type(gp[gp.col2 > 2]))
# <class '__main__.Container'>

tf = gp[gp.col2 > 2].reset_index()
print(type(tf))
# <class '__main__.Container'>

result = df[df.col1 == tf.col1]
print(type(result))
# <class '__main__.Container'>
df=pandas.DataFrame(
[(1,2)、(1,3)、(1,4)、(2,1)、(2,2,)],列=['col1','col2'])
df=集装箱(df)
df['col1'][3]=0
打印(df)
#col1 col2
# 0     1     2
# 1     1     3
# 2     1     4
# 3     2     1
# 4     2     2
gp=df.groupby('col1')。聚合(np.count\u非零)
打印(总成)
#可乐
#可乐
# 1        3
# 2        2
打印(类型(gp))
# 
打印(类型(gp[gp.col2>2]))
# 
tf=gp[gp.col2>2].reset_index()
打印(类型(tf))
# 
结果=df[df.col1==tf.col1]
打印(打印(结果))
# 

我发现unbutbu的答案对我自己的应用程序非常有用,我在jupyter笔记本中正确显示它时遇到了问题。我发现向类中添加以下方法解决了这个问题

def _repr_html_(self):
    return self.contained._repr_html_()

def _repr_latex_(self):
    return self.contained._repr_latex_()

代理对象将如何帮助您的序列化-您仍然需要以某种方式这样做。。。只需从基础继承(因为您的对象“是-a”)并从那里开始工作…a
pandas.DataFrame
有许多方法返回另一个
DataFrame
。可能很难安排您的
容器
返回另一个
容器
。@Jon基类不是serializalbe,而是pickable。扩展组件的酸洗比扩展超类的酸洗更容易。@unutbu,很好。我想我可以包装每个返回的
数据帧
。你能解释一下为什么这比继承更可取(或不可取)吗?这非常有用。我在其他地方找不到关于这个的好讨论。@user1579844:如果类B通过添加新方法扩展了类a,并且没有覆盖类a的方法,那么使用继承。如果在使用
A
的地方可以替换
B
,则使用继承。将组合用于依赖项注入(允许类
B
具有类
A2
,而不是类
a
)。有关此想法和其他想法,请参阅。有时组合或继承都不对——有时一个简单的函数是最好的。(我很想知道这里是否是这样。)@unutbu,如果我选择简单的函数解决方案,我如何定制myindicator.plot(),例如@user1579844:您将
def myplot(dataframe):…
定义为模块级函数。
def _repr_html_(self):
    return self.contained._repr_html_()

def _repr_latex_(self):
    return self.contained._repr_latex_()