在 Python 中使类不可变的方法

发布于 2024-10-17 12:20:16 字数 442 浏览 5 评论 0原文

我正在做一些分布式计算,其中多台机器在假设它们都具有不同类的相同版本的情况下进行通信。因此,使这些类不可变似乎是一个很好的设计;并不是说它必须阻止恶意用户,而是足够不可变,永远不会被意外修改。

我该怎么办呢?例如,我将如何实现一个元类,使使用它的类在定义后不可变?

>>> class A(object):
...     __metaclass__ = ImmutableMetaclass
>>> A.something = SomethingElse # Don't want this
>>> a = A()
>>> a.something = Whatever # obviously, this is still perfectly fine.

替代方法也很好,例如采用类并返回不可变类的装饰器/函数。

I'm doing some distributed computing in which several machines communicate under the assumption that they all have identical versions of various classes. Thus, it seems to be good design to make these classes immutable; not in the sense that it must thwart a user with bad intentions, just immutable enough that it is never modified by accident.

How would I go about this? For example, how would I implement a metaclass that makes the class using it immutable after it's definition?

>>> class A(object):
...     __metaclass__ = ImmutableMetaclass
>>> A.something = SomethingElse # Don't want this
>>> a = A()
>>> a.something = Whatever # obviously, this is still perfectly fine.

Alternate methods is also fine, such as a decorator/function that takes a class and returns an immutable class.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

韬韬不绝 2024-10-24 12:20:16

如果使用 __slots__ 的老技巧确实有效不适合你,这个或其某些变体可以这样做:
只需编写 __setattr__ 方法你的元类是你的守卫。在此示例中,我阻止分配新属性,但允许修改现有属性:

def immutable_meta(name, bases, dct):
    class Meta(type):
        def __init__(cls, name, bases, dct):
            type.__setattr__(cls,"attr",set(dct.keys()))
            type.__init__(cls, name, bases, dct)

        def __setattr__(cls, attr, value):
            if attr not in cls.attr:
                raise AttributeError ("Cannot assign attributes to this class")
            return type.__setattr__(cls, attr, value)
    return Meta(name, bases, dct)


class A:
    __metaclass__ = immutable_meta
    b = "test"

a = A()
a.c = 10 # this works
A.c = 20 # raises valueError

If the old trick of using __slots__ does not fit you, this, or some variant of thereof can do:
simply write the __setattr__ method of your metaclass to be your guard. In this example, I prevent new attributes of being assigned, but allow modification of existing ones:

def immutable_meta(name, bases, dct):
    class Meta(type):
        def __init__(cls, name, bases, dct):
            type.__setattr__(cls,"attr",set(dct.keys()))
            type.__init__(cls, name, bases, dct)

        def __setattr__(cls, attr, value):
            if attr not in cls.attr:
                raise AttributeError ("Cannot assign attributes to this class")
            return type.__setattr__(cls, attr, value)
    return Meta(name, bases, dct)


class A:
    __metaclass__ = immutable_meta
    b = "test"

a = A()
a.c = 10 # this works
A.c = 20 # raises valueError
乖乖公主 2024-10-24 12:20:16

不要在不可变的类上浪费时间。

您可以做的一些事情比尝试创建不可变对象要简单得多。

这里有五种单独的技术。您可以从其中挑选。任何一个都可以工作。有些组合也能发挥作用。

  1. 文档。事实上,他们不会忘记这一点。给予他们信任。

  2. 单元测试。使用处理 __setattr__作为例外。对对象状态的任何更改都会导致单元测试失败。它很简单,不需要任何复杂的编程。

  3. 重写__setattr__以在每次尝试写入时引发异常。

  4. collections.namedtuple。它们开箱即用,是不可变的。

  5. collections.Mapping。它是不可变的,但您确实需要实现一些方法才能使其工作。

Don't waste time on immutable classes.

There are things you can do that are far, far simpler than messing around with trying to create an immutable object.

Here are five separate techniques. You can pick and choose from among them. Any one will work. Some combinations will work, also.

  1. Documentation. Actually, they won't forget this. Give them credit.

  2. Unit test. Mock your application objects with a simple mock that handles __setattr__ as an exception. Any change to the state of the object is a fail in the unit test. It's easy and doesn't require any elaborate programming.

  3. Override __setattr__ to raise an exception on every attempted write.

  4. collections.namedtuple. They're immutable out of the box.

  5. collections.Mapping. It's immutable, but you do need to implement a few methods to make it work.

提笔落墨 2024-10-24 12:20:16

归档不变性的主要方法:

  1. 使用不可变结构,例如tuplefrozenset
  2. 使用@dataclass(frozen=True) 装饰器,
  3. 使用 pyndatic 的 BaseModelConfig 覆盖:
from pydantic import BaseModel

class Point(BaseModel):
    x: float
    y: float
    z: float

    class Config:
        allow_mutation = False

p = Point(x=3.14, y=2.72, z=0)

p.x = 0  # this operation raise TypeError, because the object is immutable

The main approaches for archive immutability:

  1. use immutable structures, e.g. tuple, frozenset,
  2. use @dataclass(frozen=True) decorator,
  3. use pyndatic's BaseModel with Config overriding:
from pydantic import BaseModel

class Point(BaseModel):
    x: float
    y: float
    z: float

    class Config:
        allow_mutation = False

p = Point(x=3.14, y=2.72, z=0)

p.x = 0  # this operation raise TypeError, because the object is immutable
将军与妓 2024-10-24 12:20:16

如果您不介意重复使用别人的工作:

http://packages.python.org/pysistence/

不可变的持久性(在功能上,不写入桌面意义上)数据结构。

即使您不按原样使用它们,源代码也应该提供一些灵感。例如,他们的 Expando 类在其构造函数中接受一个对象并返回它的不可变版本。

If you don't mind reusing someone else's work:

http://packages.python.org/pysistence/

Immutable persistent (in the functional, not write to desk sense) data structures.

Even if you don't use them as is, the source code should provide some inspiration. Their expando class, for example, takes an object in it's constructor and returns an immutable version of it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文