包装器类仅公开其要包装的类的属性的子集

时间:2019-03-10 05:36:10

标签: python python-3.x object

我想知道是否有一种比我现在拥有的更优雅的方式来显示包装类的属性:

class WrapperClass:
    EXPOSED_ATTRIBUTES = ["a"]

    def __init__(self):
        self.wrapped_class = Foo()

    def print_a(self):
        print(f"The value of a is :{self.wrapped_class.a}")

    def __getattr__(self, item):
        if item in self.EXPOSED_ATTRIBUTES:
            return getattr(self.wrapped_class, item)
        elif item in self.__dict__:
            return getattr(self, item)
        else:
            raise AttributeError


class Foo:
    def __init__(self):
        self.a = 1
        self.b = 2

编辑:看来我的意图不明确。包装类的目的是在我的代码和第三方库之间创建边界。我只是想减少我的代码公开的属性和方法的数量。

1 个答案:

答案 0 :(得分:1)

如果您要对包装类进行的所有操作都限制了公开字段的数量,则应利用Python动态生成类的能力。也就是说,创建一个工厂,该工厂生成仅包含暴露字段可用的新类。

当前,EXPOSED_ATTRIBUTES查找正在使用数组,并且(相对)比直接查找慢,因为每次调用__getattr_()时都要迭代整个数组,以确保属性公开。通过动态生成一个类,您的访问时间将与任何其他类相同,并且它将为您处理所有AttributeError异常。

您可以使用type(name, bases, attributes)命令来创建新类型(又名:类)。就您而言,您可以编写一个简单的工厂函数,如下所示:

def wrap(wrapped, name, exposed_attributes=[]):
  attributes = {attr:wrapped[attr] for attr in exposed_attributes}
  return type(name, (object,), attributes)

这将像这样使用

class SomeClass(object):
  def __init__(self):
    self.name = 'Joe Blow'
    self.age  = 27

WrappedClass = wrap(SomeClass, 'WrappedClass', ['name'])
wrapped_class = WrappedClass()
wrapped_class.name # 'Joe Blow'
wrapped_class.age  # AttributeError

# another wrapper on the same class
AnotherWrappedClass = wrap(SomeClass, 'AnotherWrappedClass', ['age'])
another_wrapped_class = AnotherWrappedClass()
wrapped_class.age  # 27
wrapped_class.name # AttributeError

在代码中,我们使用字典理解来构建属性字典。每个属性都指向包装的类上的一个属性。返回新类型,然后可以创建它的实例。新类仅存在公开的字段,因此尝试访问任何其他属性会抛出AttributeError,就像您期望的那样。