0

I'd like to be able to assign aliases to class names, and somehow define the aliases within the class body. So, for example, rather than doing this:

class C(object):
    pass
C1 = C

I'd like to be able to do this:

class C(object):
    __aliases__ = ['C1']

or something of the sort.

My objective is to make the information about the aliases contained within the class definition so that auto-completion works in the right environments and any kind of introspection will reveal the class' aliases (so, for example, documentation can be made to include these aliases.)

I'm guessing I can play games with jumping to the containing frame or whatever, but I'd just like to avoid any ugly trickery if that's possible.

The question is, is this possible?

4

4 回答 4

2

这是使用类装饰器执行类似操作的另一种方法 - 但别名并未像您想要的那样在类主体中指定。它与@BrenBarn 的答案非常相似,除了别名是装饰器参数——我认为这对于某些类型的使用可能更可取(而且它看起来更明确,因为它使它们更显眼)。

import sys

def aliases(*pseudonyms):
    def aliaser(cls):
        namespace = sys._getframe(1).f_globals  # Caller's globals.
        namespace.update({alias: cls for alias in pseudonyms})
        cls.aliases = pseudonyms
        return cls
    return aliaser

if __name__ == '__main__':
    @aliases('SN', 'FUBAR')
    class LongName(object):
        pass

    print(SN)                # <class '__main__.LongName'>
    print(FUBAR)             # <class '__main__.LongName'>
    print(LongName.aliases)  # ('SN', 'FUBar')

更新

PEP 487是在 Python 3.6 版中实现的,它允许通过新__init_subclass__()钩子继承来自定义类。下面的代码显示了如何使用它来完成同样的事情。

class AKA:
    """ An 'Also Known As' baseclass that allows subclasses to give themselves
        aliases.
    """
    def __init_subclass__(cls, /, aliases=(), **kwargs):
        super().__init_subclass__(**kwargs)
        cls.aliases = aliases
        globals().update({alias: cls for alias in aliases})


if __name__ == '__main__':

    class LongName(AKA, aliases=('SN', 'FUBAR')):
        pass

    print(SN)                # <class '__main__.LongName'>
    print(FUBAR)             # <class '__main__.LongName'>
    print(LongName.aliases)  # ('SN', 'FUBar')
于 2014-02-19T00:01:51.230 回答
1

似乎这将是处理元类的好工作:

class AKA(type):
    """ 'Also Known As' metaclass to create aliases for a class. """
    def __new__(cls, classname, bases, attrs):
        class_ = type(classname, bases, attrs)
        globals().update({alias: class_ for alias in attrs.get('aliases', [])})
        return class_

class C(object):
    __metaclass__ = AKA
    aliases = 'C1', 'C2'

print(C)          # <class '__main__.C'>
print(C.aliases)  # ('C1', 'C2')
print(C1)         # <class '__main__.C'>
print(C2)         # <class '__main__.C'>

注意:在 Python 3.x 中,指定元类的语法不同,需要:

class C(metaclass=AKA):
    aliases = 'C1', 'C2'
于 2013-10-29T08:50:10.137 回答
0

这是一个坏主意,但可以通过滥用类装饰器来完成:

def aliaser(cls):
    for alias in cls.aliases:
        globals()[alias] = cls
    return cls

@aliaser
class LongName(object):
    aliases = ['LN', 'Ugh']

>>> LN
<class '__main__.LongName'>

这是一种滥用,因为装饰器的目的是修改它们装饰的对象,但这仅用于其全局副作用,即在指向同一类的全局命名空间中分配其他名称。

使用风险自负!;-)

于 2013-10-29T06:34:16.273 回答
0

在类定义中定义一个 python 类“别名”

我只是回应上述问题陈述。Martineau 不接受的答案与我为提供简单别名所做的接近。但也许我们真的不想要一个别名,也许我们真的只是想采取措施摆脱一个坏名字。

起名很难。有时我们最终会得到想要更改的名称。

我正在查看一些带有不幸命名类的库代码,这些类需要弃用。例如:

class BadC: # Bad name, but changing it could break others!
    """Great implementation!"""
    

然而,大量的别名并没有帮助。指向同一个对象的多个名称意味着更多的学习,并使您的代码比它需要的更复杂。坏名声最终需要消失。

我建议进行以下更改作为警告而不破坏用户的解决方案:

class GoodC:
    """Great implementation!"""
    

class BadC(GoodC):
    """deprecated, use GoodC instead!"""
    def __init__(self, *args, **kwargs):
        import warnings
        warnings.warn(
          "BadC is deprecated, import and use GoodC instead!",
          DeprecationWarning)
        return super().__init__(*args, **kwargs)

自动完成将自动知道这些名称,并且自省 ( help(BadC)) 将立即显示您应该停止使用BadC.

现在您可以开始在其余代码中将 BadC 替换为 GoodC。

>>> GoodC()
<__main__.GoodC object at 0x7f0d0c521c88>

当您或您的用户使用 BadC 时,他们会收到警告:

>>> BadC()
__main__:4: UserWarning: The BadC name is deprecated, import and use GoodC instead!
<__main__.BadC object at 0x7f0d0c521cc0>

并且仍然能够继续使用它:

>>> BadC()
<__main__.BadC object at 0x7f0d0c521c88>

测试通常会显示这些警告,或者用户可以从文档中启用它们

在理想情况下,代码将有一个合适的测试套件,并且测试运行程序将在运行测试时隐式启用所有警告(由 unittest 模块提供的测试运行程序执行此操作)。

-Wd在不太理想的情况下,可以通过传递给 Python 解释器(这是 的简写-W default)或在环境中设置来检查应用程序是否使用了已弃用的接口PYTHONWARNINGS=default。这启用了对所有警告的默认处理,包括那些默认被忽略的警告。要更改对遇到的警告采取的操作,您可以更改传递给的参数-W(例如-W error)。有关可能的更多详细信息,请参见-W标志。

于 2018-11-09T03:50:31.573 回答