如何静默地让嵌套类知道它的外部类通过将所有必需的代码放在父类或元类中?

如何解决如何静默地让嵌套类知道它的外部类通过将所有必需的代码放在父类或元类中?

我制作了一个符号类,可以帮助我通过使用元类来制作符号常量

>>> class SymbolClass(type):
    def __repr__(self): return self.__name__

>>> class Symbol(metaclass=SymbolClass): pass

>>> class Spam(Symbol) : pass

>>> class Egg(Symbol) : pass

>>> class Banana(Symbol) : pass

>>> mydict = {Egg:"Where is the bacon",Banana:(2,3,4)}

>>> mydict
{Egg: 'Where is the bacon',Banana: (2,4)}

但是,如果我想通过将这些符号嵌套在外部类中来将其用于枚举,则需要一种方法来使符号内部类知道它们的外部类。使用当前的实现,会发生以下情况:

>>> class Animal:
        class Dog(Symbol): pass
        class Cat(Symbol): pass

>>> print(repr(Animal.Dog))
Dog

打印 str(Animal.Dog) 可以得到这个回复,但是打印 repr(Animal.Dog) 时,我想得到 Animal.Dog

那么,我怎样才能让嵌套的 Animal.DogAnimal.Cat 类知道它们的外部 Animal 类,但又不会使它们的声明复杂化。我想要所有需要的代码,隐藏在超类(SymbolAnimal 的超类)或其元类中。

解决方法

与名称相反的 __qualname__ 包括有关封闭类或函数的信息:


In [13]: class Symbol(type):
    ...:     def __repr__(cls):
    ...:         return cls.__qualname__
    ...: 
    ...: 

In [14]: class Dog(metaclass=Symbol): pass

In [15]: Dog
Out[15]: Dog

In [16]: class Animal:
    ...:     class Dog(metaclass=Symbol): pass
    ...: 

In [17]: Animal.Dog
Out[17]: Animal.Dog

当然,如果您真的想使用枚举,Python 的 enum.Enum 可以涵盖大量用例。有时我只是觉得它有点矫枉过正,并使用一些更简单的临时事物,但是一旦您求助于自定义元类,那就不再“更简单”了,并且您可能会遇到一些枚举已经解决的极端情况。

真正获取关于封闭类的信息

现在,无论枚举如何,如果想要使用元类并获得有关封闭类主体的信息 - 如本问题的标题所示,事情就变得棘手了。对于在类体定义内部,类体本身的变量是局部变量,全局变量指向模块全局变量:当封闭作用域是函数时,没有 nonlocal 的等价物。>

如果设置为类属性的对象需要有关其所有者类的信息,Python 会实现强大的 descriptor procotol,由语言内置 property 使用。您只需要在对象的类中定义一个 __get__ 方法,并注意所有者属性:不需要元类:

In [56]: class Symbol: # not a metaclass
    ...:     def __get__(self,instance,owner):
    ...:         self.owner = owner
    ...:         return self
    ...:     def __set_name__(self,owner,name):
    ...:         self.name = name
    ...:     def __repr__(self):
    ...:         return f"{self.owner.__name__}.{self.name}"
    ...: 
    ...: 

In [57]: class Animal:
    ...:     Dog = Symbol()
    ...: 

In [58]: Animal.Dog
Out[58]: Animal.Dog

现在,如果想要在嵌套类创建时获得有关封闭作用域的信息,这是可行的,但是需要内省元类'__new__ 中的堆栈,并在封闭范围是另一个类的主体。一个强烈的暗示是它是否定义了 locals__module__ 键。然后你可以访问封闭类的命名空间,但是......封闭类本身还不存在:

__qualname__

因此,如果只需要封闭类 In [40]: import inspect In [41]: class M(type): ...: def __new__(mcls,name,bases,ns,**kw): ...: enclosing_frame = inspect.stack()[1].frame ...: print (enclosing_frame.f_locals) ...: return super().__new__(mcls,**kw) ...: In [42]: class A: ...: class B(metaclass=M): pass ...: {'__module__': '__main__','__qualname__': 'A'} ,则可以使用 __name__ 并完成它。但是,如果您只想要名称,那么 __qualname__ 本身已经设置在嵌套的类命名空间中。

如果想要一个对 __qualname__ 本身的有效引用,在创建嵌套类本身时是不可能的,因为此时外部类不存在:它只会在结束时创建当然,它自己的身体的定义。

幸运的是,如果属性是类,并且在其元类中定义了 __class__ 特殊方法,则描述符机制将起作用:

__set_name__

再次:问题的最后一部分只是为了那些想要获得对外部类的引用而不是获取名称的其他目的的人。现在,检查描述符协议,使用 In [51]: class Symbol(type): ...: def __set_name__(cls,name): ...: assert name == cls.__name__ ...: cls.owner = owner # it turns out __get__ is not needed for __set_name__ to work ...: #def __get__(cls,owner): ...: # needed so the attribute is recognized as a descriptor (? - actually: to be checked) ...: # return cls ...: def __repr__(cls): ...: return f"{cls.owner.__name__}.{cls.__name__}" ...: ...: In [52]: class Animal: ...: class Dog(metaclass=Symbol): pass ...: In [53]: Animal.Dog Out[53]: Animal.Dog 方法可以提供此类信息,而根本不需要元类。

,

您需要让 AnimalEnum 继承:

from enum import Enum

class Animal(Enum):
    class Dog(Symbol): pass
    class Cat(Symbol): pass

>>> print(repr(Animal.Dog))
<Animal.Dog: Dog>

>>> print(Animal.Dog)
Animal.Dog

如果您想要不同的 str()repr(),您需要定义它们:

class Animal(Enum):
    #
    def __repr__(self):
        return "%s.%s" % (self.__class__.__name__,self._name_)
    #
    def __str__(self):
        return self._name_
    #
    class Dog(Symbol): pass
    class Cat(Symbol): pass

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res