我可以限制Python3中的对象,以便只允许我为其设置设置器的属性吗? [英] Can I restrict objects in Python3 so that only attributes that I make a setter for are allowed?

查看:56
本文介绍了我可以限制Python3中的对象,以便只允许我为其设置设置器的属性吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个叫做Node的东西. 定义"和定理"都是节点的一种,但是仅应允许定义"具有plural属性:

I have something called a Node. Both Definition and Theorem are a type of node, but only Definitions should be allowed to have a plural attribute:

class Definition(Node):


    def __init__(self,dic):
        self.type = "definition"
        super(Definition, self).__init__(dic)
        self.plural = move_attribute(dic, {'plural', 'pl'}, strict=False)


    @property
    def plural(self):
        return self._plural

    @plural.setter
    def plural(self, new_plural):
        if new_plural is None:
            self._plural = None
        else:
            clean_plural = check_type_and_clean(new_plural, str)
            assert dunderscore_count(clean_plural)>=2
            self._plural = clean_plural


class Theorem(Node):


    def __init__(self, dic):
        self.type = "theorem"
        super().__init__(dic)
        self.proofs = move_attribute(dic, {'proofs', 'proof'}, strict=False)
        # theorems CANNOT have plurals:
        # if 'plural' in self:
        #   raise KeyError('Theorems cannot have plurals.')

如您所见,定义具有plural.setter,但是定理则没有.但是,代码

As you can see, Definitions have a plural.setter, but theorems do not. However, the code

theorem = Theorem(some input)
theorem.plural = "some plural"

运行得很好,不会出现任何错误.但是,我想要提出一个错误.如您所见,我尝试在所示代码的底部手动检查复数,但这只是一个补丁.我想阻止未明确定义的ANY属性的设置.这种事情的最佳实践是什么?

runs just fine and raises no errors. But I want it to raise an error. As you can see, I tried to check for plurals manually at the bottom of my code shown, but this would only be a patch. I would like to block the setting of ANY attribute that is not expressly defined. What is the best practice for this sort of thing?

我正在寻找一个满足以下条件的答案::

I am looking for an answer that satisfies the "chicken" requirement:

我认为这不能解决我的问题.在您的两种解决方案中,我都可以 附加代码t.chicken ='hi'; print(t.chicken),它打印嗨 没有错误.我不希望用户能够化妆新的 属性,例如鸡肉.

I do not think this solves my issue. In both of your solutions, I can append the code t.chicken = 'hi'; print(t.chicken), and it prints hi without error. I do not want users to be able to make up new attributes like chicken.

推荐答案

简短的回答是是的,可以."

The short answer is "Yes, you can."

后续问题是为什么?" Python的优势之一就是非凡的动态性,并且通过限制该功能,您实际上在使您的类不再有用(但请参阅底部的编辑).

The follow-up question is "Why?" One of the strengths of Python is the remarkable dynamism, and by restricting that ability you are actually making your class less useful (but see edit at bottom).

但是,有充分的理由加以限制,如果您选择沿这条路线走,则需要修改__setattr__方法:

However, there are good reasons to be restrictive, and if you do choose to go down that route you will need to modify your __setattr__ method:

def __setattr__(self, name, value):
    if name not in ('my', 'attribute', 'names',):
        raise AttributeError('attribute %s not allowed' % name)
    else:
        super().__setattr__(name, value)

没有必要弄乱__getattr____getattribute__,因为它们不会返回不存在的属性.

There is no need to mess with __getattr__ nor __getattribute__ since they will not return an attribute that doesn't exist.

这是您的代码,稍有修改-我在Node中添加了__setattr__方法,并在DefinitionTheorem中添加了_allowed_attributes.

Here is your code, slightly modified -- I added the __setattr__ method to Node, and added an _allowed_attributes to Definition and Theorem.

class Node:

    def __setattr__(self, name, value):
        if name not in self._allowed_attributes:
            raise AttributeError('attribute %s does not and cannot exist' % name)
        super().__setattr__(name, value)


class Definition(Node):

    _allowed_attributes = '_plural', 'type'

    def __init__(self,dic):
        self.type = "definition"
        super().__init__(dic)
        self.plural = move_attribute(dic, {'plural', 'pl'}, strict=False)

    @property
    def plural(self):
        return self._plural

    @plural.setter
    def plural(self, new_plural):
        if new_plural is None:
            self._plural = None
        else:
            clean_plural = check_type_and_clean(new_plural, str)
            assert dunderscore_count(clean_plural)>=2
            self._plural = clean_plural


class Theorem(Node):

    _allowed_attributes = 'type', 'proofs'

    def __init__(self, dic):
        self.type = "theorem"
        super().__init__(dic)
        self.proofs = move_attribute(dic, {'proofs', 'proof'}, strict=False)

在使用中,它看起来像这样:

In use it looks like this:

>>> theorem = Theorem(...)
>>> theorem.plural = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
AttributeError: attribute plural does not and cannot exist

编辑

考虑了更多之后,我认为可以对您想要的东西做出一个很好的妥协,并实际上回答您的问题的一部分,即仅将允许的更改限制在二传手上,这将是:

Having thought about this some more, I think a good compromise for what you want, and to actually answer the part of your question about restricting allowed changes to setters only, would be to:

  • 在创建时使用元类检查类,并动态构建_allowed_attributes元组
  • 修改Node__setattr__,以始终允许使用至少一个前导_
  • 来修改/创建属性
  • use a metaclass to inspect the class at creation time and dynamically build the _allowed_attributes tuple
  • modify the __setattr__ of Node to always allow modification/creation of attributes with at least one leading _

这为您提供了一些保护,防止您不需要的拼写错误和创建属性,同时仍允许程序员根据自己的需要来解决或增强类.

This gives you some protection against both misspellings and creation of attributes you don't want, while still allowing programmers to work around or enhance the classes for their own needs.

好的,新的元类如下:

class NodeMeta(type):

    def __new__(metacls, cls, bases, classdict):
        node_cls = super().__new__(metacls, cls, bases, classdict)
        allowed_attributes = []
        for base in (node_cls, ) + bases:
            for name, obj in base.__dict__.items():
                if isinstance(obj, property) and hasattr(obj, '__fset__'):
                    allowed_attributes.append(name)
        node_cls._allowed_attributes = tuple(allowed_attributes)
        return node_cls

Node类有两项调整:包括NodeMeta元类,并调整__setattr__以仅阻止非下划线前导属性:

The Node class has two adjustments: include the NodeMeta metaclass and adjust __setattr__ to only block non-underscore leading attributes:

class Node(metaclass=NodeMeta):

    def __init__(self, dic):
        self._dic = dic

    def __setattr__(self, name, value):
        if not name[0] == '_' and name not in self._allowed_attributes:
            raise AttributeError('attribute %s does not and cannot exist' % name)
        super().__setattr__(name, value)

最后,Node子类TheoremDefinitiontype属性移到类名称空间中,因此设置它们没有问题-附带说明,type是不好的名称,因为它也是一个内置函数-可能是node_type代替?

Finally, the Node subclasses Theorem and Definition have the type attribute moved into the class namespace so there is no issue with setting them -- and as a side note, type is a bad name as it is also a built-in function -- maybe node_type instead?

class Definition(Node):

    type = "definition"

    ...

class Theorem(Node):

    type = "theorem"

    ...

最后,请注意:即使这种方法也不能免除某些人实际添加或更改属性的麻烦,因为仍然可以使用object.__setattr__(theorum_instance, 'an_attr', 99) -或者(甚至更简单的)可以修改_allowed_attributes;但是,如果有人要去做所有的工作,他们希望知道他们在做什么……如果没有,他们将拥有所有的东西. ;)

As a final note: even this method is not immune to somebody actually adding or changing attributes, as object.__setattr__(theorum_instance, 'an_attr', 99) can still be used -- or (even simpler) the _allowed_attributes can be modified; however, if somebody is going to all that work they hopefully know what they are doing... and if not, they own all the pieces. ;)

这篇关于我可以限制Python3中的对象,以便只允许我为其设置设置器的属性吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆