15

如何实现组合模式?我有一个Container具有属性 object的类Contained。我想通过简单地调用来重定向/允许访问Contained类的所有方法。我是否以正确的方式做正确的事?Containermy_container.some_contained_method()

我使用类似的东西:

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) 的功能。Indicator将拥有所有的方法DataFrame。我可以使用继承,但我遵循“优先组合优于继承”的建议(例如,参见:python:inheriting or composition中的答案)。不继承的一个原因是因为基类不可序列化,我需要序列化。

我找到了这个,但我不确定它是否符合我的需要。

4

2 回答 2

23

注意事项:

  • DataFrames 有很多属性。如果DataFrame属性是数字,您可能只想返回该数字。但是如果DataFrame属性是DataFrame你可能想要返回一个Container. 如果DataFrame属性是一个Series或一个描述符,我们应该怎么做?要Container.__getattr__正确实现,您确实必须为每个属性编写单元测试。
  • 还需要进行单元测试__getitem__
  • 您还必须定义和单元测试__setattr__and __setitem__, __iter__,__len__等。
  • Pickling 是序列化的一种形式,所以如果DataFrames是可腌制的,我不确定Containers 如何真正帮助序列化。

一些评论:

  • __getattr__仅当属性不在 中时才调用self.__dict__。所以你不需要if item in self.__dict__在你的__getattr__.

  • self.contained.__getattr__(item)直接调用self.contained's __getattr__方法。这通常不是您想要做的,因为它绕过了整个 Python 属性查找机制。例如,它忽略了该属性可能位于self.contained.__dict__或位于__dict__的基数之一中的可能性,self.contained.__class__或者如果item指代描述符。而是使用getattr(self.contained, item).


import pandas
import numpy as np

def tocontainer(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return Container(result)
    return wrapper

class Container(object):
   def __init__(self, df):
       self.contained = df
   def __getitem__(self, item):
       result = self.contained[item]
       if isinstance(result, type(self.contained)):
           result = Container(result)
       return result
   def __getattr__(self, item):
       result = getattr(self.contained, item)
       if callable(result):
           result = tocontainer(result)
       return result
   def __repr__(self):
       return repr(self.contained)

下面是一些随机代码,用于测试是否——至少在表面上——正确地Container委托给DataFrames 并返回Containers

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'>
于 2012-11-19T21:07:07.253 回答
1

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

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

def _repr_latex_(self):
    return self.contained._repr_latex_()
于 2016-12-08T13:12:22.353 回答