是否可以动态定义方法的名称?

时间:2014-06-16 03:34:54

标签: python

我对C,C ++,C#和Objective-C非常流利,但对Python来说却相当新。而我正在尝试使用Python + Django。我遇到的第一件事是,如果使用python 2.x,Django模型的“to string”功能是通过__unicode__完成的,而对于3.x,它是通过__str__完成的。< / p>

我想在我的类中只定义一次“to string”方法:相同的主体,根据python版本的不同签名。

例如,在C和C ++中,这可以通过预处理器宏完成(在编译时,但它可以达到目的)。在C#中,通过内省,在Objective-C中也是如此(通过运行时函数)。但是我不确定将它拉出来的“优雅”方式是在Python中。到目前为止我试过这个:

class A(models.Model):
  def _infoMethod(self):
    # blah..

class B(models.Model):
  def _infoMethod(self):
    # blah..

# ...

class N(models.Model):
  def _infoMethod(self):
    # blah..

for c in inspect.getmembers(sys.modules[__name__], inspect.isclass):
  if callable(getattr(c[1], "_infoMethod", None)):
    setattr(c[1], '__unicode__' if sys.version_info.major < 3 else '__str__', c[1]._infoMethod)

它确实有效,但是在解析所有类之后我需要迭代模块中的类,找到哪些具有该方法并为每个类动态设置“别名”,这似乎有些麻烦。我想知道是否有一种更“元”的方式。涉及在类等内部替换方法签名中的标识符部分的内容等

4 个答案:

答案 0 :(得分:3)

最简单的方法就是

class Foo(models.Model):
    def __unicode__(self):
        ...
    __str__ = __unicode__

答案 1 :(得分:2)

您可以使用类装饰器:

def deco(cls):
    setattr(cls, '__unicode__' if sys.version_info.major < 3 else '__str__', cls._infoMethod)
    return cls

@deco
class B(models.Model):
    def _infoMethod(self):
        #blah

但是,您也可以使用__str__ = __unicode__定义类,而不是花哨。

答案 2 :(得分:1)

我可能不会完全关注你,但你不能得到你想要的东西吗?

class N(models.Model):
    def _infoMethod(self):

    __str__ = _infoMethod
    __unicode__ = _infoMethod

Python 3.x有__str__,Python 2.x有__unicode__,它们都映射到相同的底层函数。

答案 3 :(得分:1)

easy 方法是通过为所有模型使用基类...

class ModelBase(models.Model):
    if python3:
        def __str__(self):
            return self._infoMethod()
    else:
        def __unicode__(self):
            return self._infoMethod()

现在只是在任何地方的子类......

class A(ModelBase):
    def _infoMethod(self):
        return 'foo'

...

当然,如果您定义了太多方法,通常不在乎......您可以只有__str____unicode__两个而不用担心Django想要打电话,因为他们都在场:

class ModelBase(models.Model):
    def __str__(self):
        return self._infoMethod()
    def __unicode__(self):
        return self._infoMethod()

此处的另一个优点是,您不需要在所有模型子类中对__str____unicode__进行别名,因为其他一些答案需要 - 只需执行一次即可#39;重新完成。

最后,如果你不想保持_infoMethod活着,你可以这样做:

class ModelBase(models.Model):
    def __unicode__(self):
        # I choose `unicode` here since it is a superset of __str__ ...
        return self.__str__()

class A(ModelBase):
    def __str__(self):
        return 'Hello World!'