如何使类属性专属于超类

2024-03-15

我有一个关于行星的大师班:

class Planet:

    def __init__(self,name):
        self.name = name
        (...)

    def destroy(self):
        (...)

我还有一些继承自的类Planet我想让其中之一无法被摧毁(而不是继承destroy功能)

Example:

class Undestroyable(Planet):

    def __init__(self,name):
        super().__init__(name)
        (...)

    #Now it shouldn't have the destroy(self) function

所以当这个运行时,

Undestroyable('This Planet').destroy()

它应该产生如下错误:

AttributeError: Undestroyable has no attribute 'destroy'

其他答案中的 mixin 方法很好,并且对于大多数情况可能更好。但尽管如此,它破坏了部分乐趣 - 也许迫使你拥有单独的行星层次结构 - 就像必须忍受两个抽象类,每个类都是“可破坏”和“不可破坏”的祖先。

第一种方法:描述符装饰器

但是Python有一个强大的机制,称为“描述符协议”,它用于从类或实例中检索任何属性 - 它甚至通常用于从实例中检索方法 - 因此,可以在它检查它是否“应该属于”该类,否则引发属性错误。

描述符协议要求每当您尝试从 Python 中的实例对象获取任何属性时,Python 将检查该属性是否存在于该对象的类中,如果存在,则该属性本身是否有一个名为__get__。如果有的话,__get__被调用(使用它被定义为参数的实例和类) - 无论它返回什么都是属性。 Python 使用它来实现方法:Python 3 中的函数有一个__get__方法,当调用时,将返回另一个可调用对象,反过来,当调用时将插入self调用原始函数时的参数。

因此,可以创建一个类,其__get__方法将决定是否将函数作为绑定方法返回,具体取决于外部类被标记为如此 - 例如,它可以检查特定标志non_destrutible。这可以通过使用装饰器用此描述符功能包装方法来完成

class Muteable:
    def __init__(self, flag_attr):
        self.flag_attr = flag_attr

    def __call__(self, func):
        """Called when the decorator is applied"""
        self.func = func
        return self

    def __get__(self, instance, owner):
        if instance and getattr(instance, self.flag_attr, False):
            raise AttributeError('Objects of type {0} have no {1} method'.format(instance.__class__.__name__, self.func.__name__))
        return self.func.__get__(instance, owner)


class Planet:
    def __init__(self, name=""):
        pass

    @Muteable("undestroyable")
    def destroy(self):
        print("Destroyed")


class BorgWorld(Planet):
    undestroyable = True

并在交互式提示上:

In [110]: Planet().destroy()
Destroyed

In [111]: BorgWorld().destroy()
...
AttributeError: Objects of type BorgWorld have no destroy method

In [112]: BorgWorld().destroy
AttributeError: Objects of type BorgWorld have no destroy method

与简单地覆盖该方法不同,这种方法会在检索属性时引发错误 - 甚至会使hasattr work:

In [113]: hasattr(BorgWorld(), "destroy")
Out[113]: False

尽管如此,如果尝试直接从类而不是从实例检索方法,它将不起作用 - 在这种情况下instance参数为__get__设置为 None,我们不能说它是从哪个类检索的 - 只是owner类,它被声明的地方。

In [114]: BorgWorld.destroy
Out[114]: <function __main__.Planet.destroy>

第二种方法:__delattr__在元类上:

在写上面的时候,我突然想到Python确实有__delattr__特殊方法。如果Planet类本身实现__delattr__我们会尝试删除destroy特定派生类上的方法,它不会工作:__delattr__gards 实例中属性的属性删除 - 如果您尝试del实例中的“destroy”方法无论如何都会失败,因为该方法位于类中。

然而,在Python中,类本身就是它的“元类”的一个实例。那通常是type。正确的__delattr__在“Planet”的元类上可以通过在类创建后发出“del UndestructiblePlanet.destroy”来使“destroy”方法的“disinheitance”成为可能。

再次,我们使用描述符协议来拥有适当的“子类上的已删除方法”:

class Deleted:
    def __init__(self, cls, name):
        self.cls = cls.__name__
        self.name = name
    def __get__(self, instance, owner):
          raise AttributeError("Objects of type '{0}' have no '{1}' method".format(self.cls, self.name))

class Deletable(type):
    def __delattr__(cls, attr):
        print("deleting from", cls)
        setattr(cls, attr, Deleted(cls, attr))


class Planet(metaclass=Deletable):
    def __init__(self, name=""):
        pass

    def destroy(self):
        print("Destroyed")


class BorgWorld(Planet):
    pass

del BorgWorld.destroy    

使用此方法,即使尝试检索或检查类本身的方法存在也将起作用:

In [129]: BorgWorld.destroy
...
AttributeError: Objects of type 'BorgWorld' have no 'destroy' method

In [130]: hasattr(BorgWorld, "destroy")
Out[130]: False

具有自定义的元类__prepare__ method.

由于元类允许自定义包含类名称空间的对象,因此可以有一个对象来响应del在类体内声明,添加Deleted描述符。

对于使用这个元类的用户(程序员)来说,几乎是一样的,但是对于del语句被允许进入类主体本身:

class Deleted:
    def __init__(self, name):
        self.name = name
    def __get__(self, instance, owner):
          raise AttributeError("No '{0}' method on  class '{1}'".format(self.name, owner.__name__))

class Deletable(type):
    def __prepare__(mcls,arg):

        class D(dict):
            def __delitem__(self, attr):
                self[attr] = Deleted(attr)

        return D()

class Planet(metaclass=Deletable):
    def destroy(self):
        print("destroyed")


class BorgPlanet(Planet):
    del destroy

(“已删除”描述符是将方法标记为“已删除”的正确形式 - 但在此方法中,它在类创建时无法知道类名)

作为类装饰器:

考虑到“已删除”描述符,我们可以简单地通知要作为类装饰器删除的方法 - 在这种情况下不需要元类:

class Deleted:
    def __init__(self, cls, name):
        self.cls = cls.__name__
        self.name = name
    def __get__(self, instance, owner):
        raise AttributeError("Objects of type '{0}' have no '{1}' method".format(self.cls, self.name))


def mute(*methods):
    def decorator(cls):
        for method in methods:
            setattr(cls, method, Deleted(cls, method))
        return cls
    return decorator


class Planet:
    def destroy(self):
        print("destroyed")

@mute('destroy')
class BorgPlanet(Planet):
    pass

修改__getattribute__机制:

为了完整起见 - 真正使 Python 达到超类上的方法和属性的是内部发生的事情__getattribute__称呼。的object的版本__getattribute__是对属性检索的优先级为“数据描述符、实例、类、基类链……”的算法进行编码的地方。

因此,更改类的这一点很容易获得“合法”属性错误,而无需在以前的方法中使用“不存在”描述符。

问题是object's __getattribute__不利用type是搜索类中属性的一个 - 如果这样做,只需实现__getattribute__在元类上就足够了。必须在实例上执行此操作以避免方法的实例查找,并在元类上执行此操作以避免元类查找。当然,元类可以注入所需的代码:

def blocker_getattribute(target, attr, attr_base):
        try:
            muted = attr_base.__getattribute__(target, '__muted__')
        except AttributeError:
            muted = []
        if attr in muted:
            raise AttributeError("object {} has no attribute '{}'".format(target, attr))
        return attr_base.__getattribute__(target, attr)


def instance_getattribute(self, attr):
    return blocker_getattribute(self, attr, object)


class M(type):
    def __init__(cls, name, bases, namespace):
        cls.__getattribute__ = instance_getattribute

    def __getattribute__(cls, attr):
        return blocker_getattribute(cls, attr, type)



class Planet(metaclass=M):
    def destroy(self):
        print("destroyed")

class BorgPlanet(Planet):
    __muted__=['destroy']  #  or use a decorator to set this! :-)
    pass
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使类属性专属于超类 的相关文章

随机推荐

  • 每次调用任何其他方法之前调用一个方法

    我有一个类 该类中有一个私有方法和许多公共方法 每次调用任何其他方法之前都必须调用此私有方法 最简单的方法是在每个方法中调用该方法 但我不喜欢这种方法 还有其他方法可以实现这一目标吗 您也许可以使用 AOP 在这里做一些事情 也许通过后锐利
  • 使用 Linq 的属性选择器和Where 查询

    我正在尝试这样做 public class SomeEntityClass public Guid MyClassProperty get set public class AnotherEntityClass public Guid An
  • 如何通过 CocoaPods 使用本地项目

    我正在努力寻找一种方法来将我们创建的 Xcode 框架打包为 Pod 该框架仅在内部使用 不公开 不在 github 上 我该如何修改 podspec从我的开发机器上的本地 Xcode 项目构建 SDK 本地 CocoaPods 依赖管理器
  • 如何检查CRAN镜像是否过时?

    建议R用户从本地CRAN镜像下载R和R包 但有些已经过时了 有没有一种简单的方法来检查存储库是否已过时 R中有什么函数可以做到这一点吗 一种方法是查看 CRANMIRROR src contrib 并按日期排序 通过在日期上单击两次 以便您
  • Java Swing Mac OSX 首选项菜单

    我正在尝试将 首选项 菜单添加到我的 Java Swing 应用程序中 但事实证明它有点令人作呕 我读过很多关于这方面的帖子和文章 听起来很简单 但是 我看到参考文献com apple eawt Application在我找到的示例中 但是
  • PHP imagepng() 正在创建损坏的图像

    我到处寻找可能的解决方案来解决我的问题 不幸的是我似乎无法弄清楚 我有一个 php 文件 它根据其他图像创建图像 我让脚本完全按照现在的方式运行 没有任何缺陷 但在摆弄其他一些文件后 它突然停止工作 并在 Firefox Chrome 和
  • Android:MediaPlayer 已定稿但未发布

    我正在我当前正在开发的应用程序中使用 MediaPlayer 类 我想在 Activity 的生命周期内保留 MediaPlayer 类的实例 我在 Activity 的 onPause 方法中释放 MediaPlayer 类中的资源 但是
  • 在带有 webpack 的 Angular cli 中,如何关闭 ng 服务上的 typescript linting

    在带有 webpack 的 Angular cli 中 如何关闭 ng 服务上的 typescript linting 我遇到一些愚蠢的错误 需要跳过 我可以做他们的任何角度 cli 设置来关闭正在检查的打字稿吗 这是我使用的应用程序 ht
  • Android setRequestProperty 在 url.openConnection()

    我有一个 Android 应用程序需要在连接中设置请求属性 这是我的代码 URL url new URL sUrl HttpURLConnection connection HttpURLConnection url openConnect
  • 删除数字字符串python [重复]

    这个问题在这里已经有答案了 对于我的作业 我必须创建一个函数 该函数返回一个与给定字符串相同的新字符串 但删除了数字 示例 删除数字 abc123 将返回字符串 abc 我已经尝试了几乎所有我能想到的方法 但它不能正常工作 def test
  • AWS S3 Glacier - 以编程方式启动恢复

    我一直在编写一个网络应用程序 使用 s3 进行存储 使用 Glacier 进行备份 所以我设置了生命周期策略来存档它 现在我想编写一个列出存档文件的网络应用程序 用户应该能够从中启动恢复 然后在恢复完成后收到一封电子邮件 现在我遇到的麻烦是
  • 在使用 Docker 配置构建代理之前,Teamcity Build 不会运行?

    我为我的 Teamcity 管道创建了一个新版本 我第一次使用 Docker buildstep 设置完所有内容后 我意识到构建代理似乎还没有准备好 我知道我的代理似乎还没有准备好使用 docker 进行构建but没有人真正告诉我如何做到这
  • 在java中解析XML时没有工作ID属性

    我目前正在开发一个图形 XML 编辑器 对于该编辑器 我必须能够通过其 ID 属性访问各个元素 我已经建立了一个 XML 模式 在其中定义了 ID 属性 我使用 javax xml parsers DocumentBuilderFactor
  • 使用拆分器使 ItemsControl 子项可调整大小

    我想将小部件插入我的ItemsControl并使它们可调整大小 我该如何实现这一目标 这是我的 XAML
  • 使用 numpy 实现最大/平均池化(带步长)

    我想知道如何使用 numpy 实现简单的最大 均值池 我正在读书使用 numpy 进行最大池化和平均池化 https stackoverflow com questions 42463172 how to perform max mean
  • 如何动态更改 SQLAlchemy 声明性模型上的列类型?

    我在生产中运行 mysql 但想在内存数据库中的 sqlite 中运行简单的测试 旧版 mysql 数据库的表中包含 mysql 特定类型的列 这些列是在声明性模型中声明的 子类 declarative base 我想运行一些简单的测试而不
  • VB.Net Linq to Entities Null 比较 - 'Is Nothing' 或 '= Nothing'?

    我们在 VB Net 中有多个项目 使用 Net Framework 4 和 Linq to Entities 进行许多 SQL 查询 迁移到 EF 对我们来说是一个新的转变 使用它大约 4 6 个月 并且得到了高层管理人员的支持 因为我们
  • 获取函数的返回值

    假设我有一些运行一些代码的函数 然后return一些东西 像这样 function something some code return some whatever 因此 如果我想提取在函数中生成的数据 的新值 some 我该怎么办呢 例如
  • 如何将 Intl.NumberFormat 与本机反应一起使用?

    我试图在本机反应中将数字转换为价格格式 如何使用https developer mozilla org en US docs Web JavaScript Reference Global Objects NumberFormat http
  • 如何使类属性专属于超类

    我有一个关于行星的大师班 class Planet def init self name self name name def destroy self 我还有一些继承自的类Planet我想让其中之一无法被摧毁 而不是继承destroy功能