【问题标题】:Namespaces inside class in Python3Python3中类内的命名空间
【发布时间】:2018-01-21 15:02:58
【问题描述】:

我是 Python 新手,我想知道是否有任何方法可以将方法聚合到“子空间”中。我的意思类似于这种语法:

smth = Something()
smth.subspace.do_smth()
smth.another_subspace.do_smth_else()

我正在编写一个 API 包装器,我将有很多非常相似的方法(只是不同的 URI),所以我认为最好将它们放在一些引用 API 请求类别的子空间中。换句话说,我想在一个类中创建命名空间。我不知道这在 Python 中是否可行,并且知道在 Google 中寻找什么。

我将不胜感激。

【问题讨论】:

  • 这些真的需要是方法吗?他们需要访问任何州吗?你可以只嵌套模块吗?
  • do_smth 这样的方法是否需要像常规方法一样访问实例?
  • 我将登录信息存储为实例的属性。我需要从每种方法中访问它们。也许有更好的方法来存储它们。
  • 有趣的概念。我想你可以通过在 __init__ 方法中执行 self.subspace = self.another_subspace = self 来伪造它。当然,这不是创建单独的名称空间,尽管它确实允许您描述的调用语法。 OTOH,它不会阻止人们做smth.do_smth()
  • 不会Something.__init__(... self.subclass = Subclass(owner=self), self.another_subclass = AnotherSubclass(owner=self...,有点实现这个?子类可以从它们的所有者那里查找他们需要的任何信息,并且您的命名空间将符合您的意图。子类将有一个 do_smth 方法。

标签: python python-3.x class namespaces


【解决方案1】:

执行此操作的一种方法是将subspaceanother_subspace 定义为返回分别提供do_smthdo_smth_else 的对象的属性:

class Something:
    @property
    def subspace(self):
        class SubSpaceClass:
            def do_smth(other_self):
                print('do_smth')
        return SubSpaceClass()

    @property
    def another_subspace(self):
        class AnotherSubSpaceClass:
            def do_smth_else(other_self):
                print('do_smth_else')
        return AnotherSubSpaceClass()

你想要什么:

>>> smth = Something()
>>> smth.subspace.do_smth()
do_smth
>>> smth.another_subspace.do_smth_else()
do_smth_else

根据您打算使用这些方法的目的,您可能希望将 SubSpaceClass 设为单例,但我怀疑性能提升是否值得。

【讨论】:

  • 这是一个非常优雅的解决方案,可以按预期工作。谢谢。
  • 这行得通,但每次访问smth.subspacesmth.another_subspace 时都会创建一个新的内部类对象。您可以通过在方法中打印 other_self 来查看。 OTOH,我想这并不比实例方法通常在每次调用时都重新绑定这一事实更糟。但是如果这些内部类包含很多方法,那么每次都必须重新构建它们会减慢速度。
  • @mDevv:我不同意。正如 PM 所指出的,这每次都会创建一个新的类对象。至少将这些类移出方法。接下来,您还必须担心共享状态。相反,我会考虑重构您的 API 以不需要命名空间?
  • 如前所述,如果性能是一个问题,您可以将它们设置为单例,但对于大多数应用程序来说确实不应该。
  • 当解决方案涉及新的类实例化时,我相信它会增加不必要的复杂性
【解决方案2】:

几年前我有这个需求并想出了这个:

class Registry:
    """Namespace within a class."""

    def __get__(self, obj, cls=None):
        if obj is None:
            return self
        else:
            return InstanceRegistry(self, obj)

    def __call__(self, name=None):

        def decorator(f):
            use_name = name or f.__name__
            if hasattr(self, use_name):
                raise ValueError("%s is already registered" % use_name)
            setattr(self, name or f.__name__, f)
            return f

        return decorator


class InstanceRegistry:
    """
    Helper for accessing a namespace from an instance of the class.

    Used internally by :class:`Registry`. Returns a partial that will pass
    the instance as the first parameter.
    """

    def __init__(self, registry, obj):
        self.__registry = registry
        self.__obj = obj

    def __getattr__(self, attr):
        return partial(getattr(self.__registry, attr), self.__obj)


# Usage:

class Something:
    subspace = Registry()
    another_subspace = Registry()

@MyClass.subspace()
def do_smth(self):
    # `self` will be an instance of Something
    pass

@MyClass.another_subspace('do_smth_else')
def this_can_be_called_anything_and_take_any_parameter_name(obj, other):
    # Call it `obj` or whatever else if `self` outside a class is unsettling
    pass

在运行时:

>>> smth = Something()
>>> smth.subspace.do_smth()
>>> smth.another_subspace.do_smth_else('other')

这与 Py2 和 Py3 兼容。在 Py3 中可以进行一些性能优化,因为 __set_name__ 告诉我们命名空间的名称并允许缓存实例注册表。

【讨论】:

    猜你喜欢
    • 2017-05-28
    • 1970-01-01
    • 2020-02-19
    • 1970-01-01
    • 2010-11-21
    • 2023-03-25
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    相关资源
    最近更新 更多