当前位置: 首页 > 面试题库 >

使用类方法而不是具有相同名称的实例方法

昝成弘
2023-03-14
问题内容

我有以下片段:

class Meta(type):
    def __getattr__(self, name):
        pass

class Klass(object):
    __metaclass__ = Meta

    def get(self, arg):
        pass

现在,如果我这样做:

kls = Klass()
kls.get('arg')

一切都按预期工作(get调用了实例方法)。

但是,如果我这样做:

Klass.get('arg')

再次找到实例方法并给出异常,因为它被视为未绑定方法。

我该如何调用以Klass.get('arg')遍历__getattr__元类中的定义?我需要这样做是因为我想将一个类上调用的所有方法代理到另一个对象(这将在中完成__getattr__)。


问题答案:

您必须在类型上查找方法,然后self手动传入first()参数:

type(Klass).get(Klass, 'arg')

这个问题正是使用此路径查找特殊方法名称的原因; 如果Python不这样做,则自定义类本身将不可哈希或不可表示。

可以
利用这一事实;而不是使用get()方法,而是使用__getitem__,重载[..]索引语法并让Pythontype(ob).methodname(ob, *args)为您起舞:

class Meta(type):
    def __getitem__(self, arg):
        pass

class Klass(object):
    __metaclass__ = Meta

    def __getitem__(self, arg):
        pass

然后Klass()['arg']Klass['arg']正常工作。

但是,如果您必须Klass.get()采取不同的行为(并且被拦截Meta.__getattribute__),则必须在您的Klass.get方法中明确处理此问题;如果在类上调用它,则将减少一个参数,您可以利用它并返回对该类的调用:

_sentinel = object()

class Klass(object):
    __metaclass__ = Meta

    def get(self, arg=_sentinel):
        if arg=_sentinel:
            if isinstance(self, Klass):
                raise TypeError("get() missing 1 required positional argument: 'arg'")
            return type(Klass).get(Klass, self)
        # handle the instance case ...

您还可以在模仿方法对象的描述符中处理此问题:

class class_and_instance_method(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, cls=None):
        if instance is None:
            # return the metaclass method, bound to the class
            type_ = type(cls)
            return getattr(type_, self.func.__name__).__get__(cls, type_)
        return self.func.__get__(instance, cls)

并将其用作装饰器:

class Klass(object):
    __metaclass__ = Meta

    @class_and_instance_method
    def get(self, arg):
        pass

如果没有实例绑定到,它将把查找重定向到元类:

>>> class Meta(type):
...     def __getattr__(self, name):
...         print 'Meta.{} look-up'.format(name)
...         return lambda arg: arg
... 
>>> class Klass(object):
...     __metaclass__ = Meta
...     @class_and_instance_method
...     def get(self, arg):
...         print 'Klass().get() called'
...         return 'You requested {}'.format(arg)
... 
>>> Klass().get('foo')
Klass().get() called
'You requested foo'
>>> Klass.get('foo')
Meta.get look-up
'foo'

装饰器的应用可以在元类中完成:

class Meta(type):
    def __new__(mcls, name, bases, body):
        for name, value in body.iteritems():
            if name in proxied_methods and callable(value):
                body[name] = class_and_instance_method(value)
        return super(Meta, mcls).__new__(mcls, name, bases, body)

然后您可以使用此元类将方法添加到类中,而不必担心委托:

>>> proxied_methods = ('get',)
>>> class Meta(type):
...     def __new__(mcls, name, bases, body):
...         for name, value in body.iteritems():
...             if name in proxied_methods and callable(value):
...                 body[name] = class_and_instance_method(value)
...         return super(Meta, mcls).__new__(mcls, name, bases, body)
...     def __getattr__(self, name):
...         print 'Meta.{} look-up'.format(name)
...         return lambda arg: arg
... 
>>> class Klass(object):
...     __metaclass__ = Meta
...     def get(self, arg):
...         print 'Klass().get() called'
...         return 'You requested {}'.format(arg)
... 
>>> Klass.get('foo')
Meta.get look-up
'foo'
>>> Klass().get('foo')
Klass().get() called
'You requested foo'


 类似资料:
  • 问题内容: 在Java中,实例变量和方法可以具有相同的名称而没有任何不稳定或冲突吗? 我想确保是否可以摆脱它的编译问题,以免造成任何错误。 问题答案: 是的,这很好,主要是因为在语法上,它们的用法不同。

  • 问题内容: 我说错了 不推荐使用的:与类相同名称的方法在将来的PHP版本中将不再是构造函数;TSStatus在第10行的C:\ Program Files(x86)\ Zend \ Apache24 \ htdocs \ viewer \ modules \ tsstatus \ tsstatus.php中已弃用构造函数 TSStatus类是第10行,并在底部的TSStatus显示 问题答案: 如

  • 问题内容: 以下代码可以正常工作。在两个不同的结构上操作并打印该结构的字段的两种方法: 在控制台中显示所需的输出: 现在 ,如果我以以下方式更改方法签名,则会出现编译错误。我只是将方法的接收者移动到方法的参数: 我什至无法编译程序: 问 :为什么 当 方法具有相同的名称和Arity 时 ,我可以在接收器中互换结构类型,而不能在参数中互换结构类型? 问题答案: 因为Go不支持在其参数类型上重载用户定

  • 我在一次面试中被问到以下问题: 问题:名称和签名相同但返回类型不同的方法。他问我,可能吗?这种类型叫什么。 有人能告诉我以下情况吗: > 上面的事情在任何情况下都是可能的(至少像一个在基类中,一个在派生类中?)如果是,是什么类型?比如编译或运行时多态? 在编译时多态性中,如果方法的返回类型与签名也不同,该怎么办?但只有函数的名称是相同的。还是编译时多态性吗? 在重写中,如果我有不同的返回类型,但方

  • 我有一个场景,其中有两个具有相同方法名称的不同标记。但它在Swagger编辑器中抛出了一个语法错误,称为“重复的映射键”。 在OpenAPI中编写此内容的正确方法是什么?

  • 我可以看到它不工作,因为我尝试了它。我只是无法解释为什么一定要这样。 第二个方法来自一个类,该类是实现第一个getValue()方法的类的子类。 为什么同名不足以覆盖该方法?我想到的唯一论点是,它将反对“是一个”关系,因为用A扩展的第二个方法的类必须具有与A相同的能力,如果你重写返回类型,你就打破了那个法律,对吧?

  • 问题内容: 在编译此文件时说 testClass.cpp:9:声明`bool Test :: isVal’ testClass.cpp:3:与先前的声明“ bool Test :: isVal()”冲突 虽然同样适用于Java 为什么在C ++中而不在Java中会发生编译错误? 问题答案: 因为C ++不是Java。您可以使用会员的地址: 因此,除了可以重载成员函数之外,不能让两个成员具有相同的名

  • 我说的是Java,但这个概念也适用于其他语言-- 我们在同一个类上有两个方法,它们具有相同的基本功能,但提供不同的返回类型。这两种方法都会给你所有的东西,但一个得到一个可以提供对所有东西的访问的可迭代的,一个得到一个包含所有东西的集合。 我们知道你不能这样做(因为它不会编译): 那么,有人想过如何命名这些方法吗?这似乎是一个简单的解决方法: 然而,这显然有些冗长,可能不是最好的解决方案。我问的这个