类方法和实例方法同名

2024-04-12

我想做这样的事情:

class X:

    @classmethod
    def id(cls):
        return cls.__name__

    def id(self):
        return self.__class__.__name__

现在打电话id()对于类或其实例:

>>> X.id()
'X'
>>> X().id()
'X'

显然,这个确切的代码不起作用,但是有类似的方法让它起作用吗?

或者有任何其他解决方法可以在没有太多“hacky”东西的情况下获得这种行为?


类和实例方法位于同一个命名空间中,您不能重复使用这样的名称;最后的定义id在这种情况下将会获胜。

类方法将继续在实例上工作,但是,有no need创建一个单独的实例方法;只需使用:

class X:
    @classmethod
    def id(cls):
        return cls.__name__

因为该方法继续绑定到类:

>>> class X:
...     @classmethod
...     def id(cls):
...         return cls.__name__
... 
>>> X.id()
'X'
>>> X().id()
'X'

这是明确记录的:

它可以在类上调用(例如C.f())或在实例上(例如C().f())。除了其类之外,该实例将被忽略。

如果您确实需要区分绑定到类和实例

如果您需要一种根据使用地点而有不同工作方式的方法;在类上访问时绑定到类,在实例上访问时绑定到实例,您需要创建一个自定义描述符对象.

The 描述符API https://docs.python.org/3/reference/datamodel.html#implementing-descriptorsPython 如何将函数绑定为方法,并绑定classmethod类的对象;看到描述符指南 https://docs.python.org/3/howto/descriptor.html.

您可以通过创建一个具有以下属性的对象来为方法提供自己的描述符__get__方法。这是一个简单的方法,它根据上下文切换方法绑定的内容,如果第一个参数为__get__ is None,那么描述符被绑定到一个类,否则它被绑定到一个实例:

class class_or_instancemethod(classmethod):
    def __get__(self, instance, type_):
        descr_get = super().__get__ if instance is None else self.__func__.__get__
        return descr_get(instance, type_)

这重复使用了classmethod并且仅重新定义它如何处理绑定,将原始实现委托给instance is None,以及标准函数__get__否则实施。

请注意,在方法本身中,您可能必须测试它所绑定的内容。isinstance(firstargument, type)对此来说是一个很好的测试:

>>> class X:
...     @class_or_instancemethod
...     def foo(self_or_cls):
...         if isinstance(self_or_cls, type):
...             return f"bound to the class, {self_or_cls}"
...         else:
...             return f"bound to the instance, {self_or_cls"
...
>>> X.foo()
"bound to the class, <class '__main__.X'>"
>>> X().foo()
'bound to the instance, <__main__.X object at 0x10ac7d580>'

另一种实现可以使用two函数,一个用于绑定到类时,另一个用于绑定到实例时:

class hybridmethod:
    def __init__(self, fclass, finstance=None, doc=None):
        self.fclass = fclass
        self.finstance = finstance
        self.__doc__ = doc or fclass.__doc__
        # support use on abstract base classes
        self.__isabstractmethod__ = bool(
            getattr(fclass, '__isabstractmethod__', False)
        )

    def classmethod(self, fclass):
        return type(self)(fclass, self.finstance, None)

    def instancemethod(self, finstance):
        return type(self)(self.fclass, finstance, self.__doc__)

    def __get__(self, instance, cls):
        if instance is None or self.finstance is None:
              # either bound to the class, or no instance method available
            return self.fclass.__get__(cls, None)
        return self.finstance.__get__(instance, cls)

这是一个带有可选实例方法的类方法。像使用一样使用它property目的;装饰实例方法@<name>.instancemethod:

>>> class X:
...     @hybridmethod
...     def bar(cls):
...         return f"bound to the class, {cls}"
...     @bar.instancemethod
...     def bar(self):
...         return f"bound to the instance, {self}"
... 
>>> X.bar()
"bound to the class, <class '__main__.X'>"
>>> X().bar()
'bound to the instance, <__main__.X object at 0x10a010f70>'

就我个人而言,我的建议是谨慎使用它;根据上下文改变行为的完全相同的方法可能会令人困惑。然而,也有这样的用例,例如 SQLAlchemy 区分 SQL 对象和 SQL 值,其中模型中的列对象像这样切换行为;看到他们的混合属性文档 https://docs.sqlalchemy.org/en/13/orm/extensions/hybrid.html#module-sqlalchemy.ext.hybrid。其实现遵循与我完全相同的模式hybridmethod上面的类。


以下是根据请求提供的上述内容的类型提示版本。这些要求您的项目具有typing_extensions https://pypi.org/project/typing-extensions/安装:

from typing import Generic, Callable, TypeVar, overload
from typing_extensions import Concatenate, ParamSpec, Self

_T = TypeVar("_T")
_R_co = TypeVar("_R_co", covariant=True)
_R1_co = TypeVar("_R1_co", covariant=True)
_R2_co = TypeVar("_R2_co", covariant=True)
_P = ParamSpec("_P")


class class_or_instancemethod(classmethod[_T, _P, _R_co]):
    def __get__(
        self, instance: _T, type_: type[_T] | None = None
    ) -> Callable[_P, _R_co]:
        descr_get = super().__get__ if instance is None else self.__func__.__get__
        return descr_get(instance, type_)


class hybridmethod(Generic[_T, _P, _R1_co, _R2_co]):
    fclass: Callable[Concatenate[type[_T], _P], _R1_co]
    finstance: Callable[Concatenate[_T, _P], _R2_co] | None
    __doc__: str | None
    __isabstractmethod__: bool

    def __init__(
        self,
        fclass: Callable[Concatenate[type[_T], _P], _R1_co],
        finstance: Callable[Concatenate[_T, _P], _R2_co] | None = None,
        doc: str | None = None,
    ):
        self.fclass = fclass
        self.finstance = finstance
        self.__doc__ = doc or fclass.__doc__
        # support use on abstract base classes
        self.__isabstractmethod__ = bool(getattr(fclass, "__isabstractmethod__", False))

    def classmethod(self, fclass: Callable[Concatenate[type[_T], _P], _R1_co]) -> Self:
        return type(self)(fclass, self.finstance, None)

    def instancemethod(self, finstance: Callable[Concatenate[_T, _P], _R2_co]) -> Self:
        return type(self)(self.fclass, finstance, self.__doc__)

    @overload
    def __get__(self, instance: None, cls: type[_T]) -> Callable[_P, _R1_co]: ...

    @overload
    def __get__(self, instance: _T, cls: type[_T] | None = ...) -> Callable[_P, _R1_co] | Callable[_P, _R2_co]: ...

    def __get__(self, instance: _T, cls: type[_T] | None = None) -> Callable[_P, _R1_co] | Callable[_P, _R2_co]:
        if instance is None or self.finstance is None:
            # either bound to the class, or no instance method available
            return self.fclass.__get__(cls, None)
        return self.finstance.__get__(instance, cls)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

类方法和实例方法同名 的相关文章

随机推荐

  • 切片 Pandas 时出现值错误

    我有一个 DataFrame 我想使用 string contains 方法 我相信当我阅读时我已经找到了如何做到这一点pandas dataframe 按部分字符串选择 https stackoverflow com questions
  • android:如何检查应用程序是否在后台运行2

    我有解决办法 public static boolean isApplicationSentToBackground final Context context ActivityManager am ActivityManager cont
  • 如何使用expo文件系统保存图库中的二维码

    我正在使用 expo 开发一个 React Native 项目 它包括创建一个二维码 我已经完成了 将二维码转换为图像 然后将其保存到图库 我想将 QR 码转换为图像 然后将其保存到我的图库中或共享 在下面给出的代码中 在编译时会出现错误
  • C++ 将基础对象类型转换为派生对象

    所以我有一个动态分配的基类数组 我在数组中存储了其派生类的一些对象 学生 基 类及其派生类都有一个getInfo 函数 显然派生类已经覆盖了该基类getInfo 目标是使用getinfo从基类中获取函数 然后将派生类的两个对象键入类 返回派
  • LINQ Count ..最好的方法

    我的公司刚刚开始使用 LINQ 但我仍然对 LINQ 命令和 SQL 的抽象性 如果这是一个词的话 有一些疑问 我的问题是 Dim query From o In data Addresses Select o Name Count 在我看
  • 错误:参数类型不兼容

    我正在用 C 写一个列表 以下是来源 include
  • Android Volley 库无法处理 204 和空主体响应

    我正在使用最新的 Volley 库 当我的 api 返回 204 且响应中没有正文时 我遇到了问题 BasicNetwork java 中的以下代码似乎未按预期工作 Some responses such as 204s do not ha
  • 在 junit 中模拟 System.getenv 调用时遇到问题

    我正在尝试使用 junit 和mockito 对此非常新 为 Spring Boot 应用程序编写单元测试 基本上在我的代码中 我已经为 manifest yml 文件 用于部署 中的特定 URL 指定了一个环境变量 我可以通过它访问Str
  • Object.assign 未按预期工作

    我有一个名为 bookings 的对象 其中有几个属性 我想使用 Object assign 进行扩展 如下所示 let data Object assign booking hiw event hiw booking locale tip
  • 如何在启用视觉样式的情况下将控件渲染为看起来像 ComboBox?

    我有一个模仿的控件ComboBox 我想渲染该控件以便该控件border看起来像标准的Windows 组合框 具体来说 我遵循了 MSDN 文档 除了禁用控件时的渲染之外 所有控件的渲染都是正确的 需要明确的是 这是针对具有视觉风格已启用
  • ASP .NET 单例

    只是想确保我在这里没有假设一些愚蠢的事情 在 ASP Net Web 应用程序中实现单例模式时 静态变量范围仅适用于当前用户会话 对吧 如果第二个用户正在访问该站点 那么它是不同的内存范围 静态成员仅具有当前工作进程的作用域 因此与用户无关
  • 为什么 LINQPad 将枚举整数值转储为字符串?

    我使用 LinqPad 来测试一些 Enum 函数 但在使用 Dump 时没有得到像我预期的整数 为什么 ToList 解决了这个问题 void Main Enum GetValues typeof Options Cast
  • Gas 与 nasm:哪个汇编器生成最好的代码?

    这两种工具都将汇编指令直接翻译成机器代码 但是是否有可能确定哪一个生成最快 最干净的代码 当你用汇编程序编写时 您正在准确地描述生成的指令所以它不依赖于汇编器 这取决于你 您编写的助记符与机器代码中的实际指令之间存在一一对应的关系
  • 原始(二进制)数据太大而无法写入磁盘。如何按块写入磁盘(附加)?

    我在 R 中有一个很大的原始向量 即二进制数据数组 我想将其写入磁盘 但我收到一条错误消息 告诉我该向量太大 这是一个可重现的示例和我收到的错误 gt writeBin raw 1024 1024 1024 2 test bin Error
  • jqgrid按文本问题对列进行排序

    我在我的网站中使用了 jqgrid 但遇到了一个问题 我做了什么 name type index type width 40 editable true edittype select sorttype text editoptions v
  • SASS 将特定属性从父级扩展/共享/继承到子级,反之亦然

    是否可以将所选 特定属性从父级扩展到 共享给子级 例如不创建变量 main container padding 20px margin 20px ul padding parentPadding margin 0 或相反亦然 就你而言 ma
  • 在 Fortran 中读取行数已知但每行条目数未知的数据文件

    如何读取包含已知行数但每行中的条目数未知的数据文件 例如如果我的数据文件包含类似的内容 1 3 4 5 6 7 8 9 1 3 5 6 4 5 6 7 8 3 5 6 7 8 4 5 7 8 即三行 但每行中的数据未知 有一次我需要来自一行
  • 引导断点...需要一些说明“xs sm md lg”

    所以 在网上查找 我看到一些最近的文章指出xs断点是480px及以下 其他 声明767及以下 我的理解 可能不正确 xs 适用于手机 480 像素及以下 col sm 适用于平板电脑 480 像素至 767 像素 等 然而 当我应用 hid
  • PHP:获取 HTTP 协议版本(HTTP/1.1 与 HTTP/2)

    到目前为止 我的 php 应用程序到处都采用 HTTP 1 1 所以我定义了所有标题 如下所示 header HTTP 1 1 500 Internal Server Error 但现在我的服务器也支持 HTTP 2 我想使用正确的 HTT
  • 类方法和实例方法同名

    我想做这样的事情 class X classmethod def id cls return cls name def id self return self class name 现在打电话id 对于类或其实例 gt gt gt X id