Python的等效NET的密封类的(Python's equivalent of .Net&

2019-09-03 08:00发布

蟒蛇是否有类似的密封类东西吗? 我相信,它也被称为最后一类,在java中。

换句话说,在Python中,我们可以标记一个类,所以它永远不能被继承或扩大后? 蟒蛇没有考虑过具有这样的功能? 为什么?

免责声明

实际上是试图理解为什么密封类甚至存在。 答案在这里 (和很多 , 很多 , 很多 , 很多 , 很多 , 真的很多其他地方)没有满足我的人,所以我试图从不同的角度看。 请避免理论这个问题的答案,并专注于冠军! 或者,如果你坚持,至少请给一个很好的和实际的C#一个密封类的实例,指出哪些将打破大的时候,如果有人启封。

我在任何一种语言的专家,但我知道两者兼而有之。 就在昨天,而IE编程编码我知道密封类的存在。 现在我想知道如果Python有任何等同于。 我相信这是一个很好的理由它的存在,但我真的没有得到它。

Answer 1:

您可以使用元类来防止子类:

class Final(type):
    def __new__(cls, name, bases, classdict):
        for b in bases:
            if isinstance(b, Final):
                raise TypeError("type '{0}' is not an acceptable base type".format(b.__name__))
        return type.__new__(cls, name, bases, dict(classdict))

class Foo:
    __metaclass__ = Final

class Bar(Foo):
    pass

得到:

>>> class Bar(Foo):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __new__
TypeError: type 'Foo' is not an acceptable base type

所述__metaclass__ = Final线使得Foo类“密封”。

请注意,您会在.NET作为业绩衡量使用密封类; 因为不会有任何的子类方法可以直接处理。 Python的方法查找工作非常不同,并没有什么优势或劣势,当谈到方法查找,使用像上面的例子元类。



Answer 2:

Python中确实有不能扩展类,如boolNoneType

>>> class ExtendedBool(bool):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type 'bool' is not an acceptable base type

然而,这样的类不能从Python代码创建的。 (在CPython的C API,它们由不设置创建Py_TPFLAGS_BASETYPE标志)。

Python的3.6将引入__init_subclass__特殊的方法; 从它产生错误会阻止创建子类。 对于老版本,可以使用元类。

不过,最“Python化”的方式来限制类的使用是记录如何不应该使用。



Answer 3:

之前我们谈论的Python,让我们来谈谈“密封”:

我也听说净的优势密封/ Java的最终/ C ++完全-非虚拟类是性能。 我听到它从微软的.Net开发,所以也许这是真的。 如果你正在构建一个沉重的使用,高性能敏感的应用程序或框架,你可能想在或真正的,异形的瓶颈附近,以密封类屈指可数。 尤其是你正在使用自己的代码中的类。

对于软件的大多数应用中,密封类,其他球队消耗作为一个框架/库的一部分/ API是有点......奇怪。

这主要是因为有一个简单的变通任何密封类,反正。

我教“基本测试驱动开发”课程,并在这三个语言,我提出这样一个密封类的消费者把它包在具有完全相同的方法签名一个委托代理,但他们覆盖,能够(虚拟)所以开发者可以创建测试双打这些缓慢,非确定性,或副作用诱导外部依赖性。

[警告:以下魅意幽默。 您激活幽默子程序的感觉阅读。 我也知道有些情况下密封/最终是必要的情况下。]

代理(这是不是测试代码)有效开封(重新虚拟化)类,导致v表查找故障,并可能代码效率不高(除非编译器优化足以胜任在线代表团)。 其优点是可以有效地测试自己的代码,节省了活生生的调试时间人周(相比之下,节约您的应用程序有几百万微秒)每月... [免责声明:这只是一个WAG。 是的,我知道,你的应用程序是特殊的。 ; - ]

所以,我的建议:(1)相信你的编译器的优化,(2)停止创建你为了建立不必要的密封/最后的/非虚拟类是(a)勉强维持的性能每微秒在一个地方,很可能不您的瓶颈反正(键盘,互联网......),或(b)在你的团队的“初级开发人员”(耶......我已经看到了,太)创造某种误导编译时的约束。

哦,和(3)第一写测试。 ;-)

好吧,是的,总是有链接时嘲讽,太(如TypeMock)。 你让我。 来吧,密封类。 Whatevs。

返回的Python:这有一个黑客,而不是一个关键字可能是Python中的纯虚拟性的反映这一事实。 它只是没有“自然”。

顺便说一句,我来到了这个问题,因为我有相同的问题。 工作我曾经那么具有挑战性和现实的遗留代码实验室的Python的端口上,我想知道Python是否有这样一个恶劣的关键字作为密封或最后(我使用它们在Java,C#和C ++课程作为挑战单元测试)。 显然,事实并非如此。 现在,我必须找到一些关于未经测试的Python代码同样具有挑战性。 嗯...



Answer 4:

在目的类似于密封类,并减少存储器使用(有用__slots__的用法? )是__slots__属性,其防止猴修补的类。 因为当元类__new__被调用时,为时已晚把一个__slots__进级,我们必须把它放进命名空间在第一时间点可能,即在__prepare__ 。 此外,这将引发该类型错误早一点。 使用MCS为isinstance比较删除的必要性本身硬编码元类的名称。 缺点是所有未开槽属性为只读。 因此,如果我们想在初始化过程中或以后设置特定的属性,他们有专门开槽。 这是可行的例如通过使用动态元类服用时隙作为参数。

def Final(slots=[]):
    if "__dict__" in slots:
        raise ValueError("Having __dict__ in __slots__ breaks the purpose")
    class _Final(type):
        @classmethod
        def __prepare__(mcs, name, bases, **kwargs):   
            for b in bases:
                if isinstance(b, mcs):
                    msg = "type '{0}' is not an acceptable base type"
                    raise TypeError(msg.format(b.__name__))

            namespace = {"__slots__":slots}
            return namespace
    return _Final

class Foo(metaclass=Final(slots=["_z"])):
    y = 1    
    def __init__(self, z=1):       
        self.z = 1

    @property
    def z(self):
        return self._z

    @z.setter
    def z(self, val:int):
        if not isinstance(val, int):
            raise TypeError("Value must be an integer")
        else:
            self._z = val                

    def foo(self):
        print("I am sealed against monkey patching")

其中覆盖foo.foo的尝试将抛出AttributeError: 'Foo' object attribute 'foo' is read-only ,并尝试添加foo.x将引发AttributeError: 'Foo' object has no attribute 'x' 。 的极限功率__slots__将继承时被打破,但由于富有元类决赛,你不能继承它。 这也将是当字典是打破插槽 ,所以我们的情况下,抛出一个ValueError。 最后,定义getter和setter方法为开槽的属性可以限制用户如何能够覆盖它们。

foo = Foo()
# attributes are accessible
foo.foo()
print(foo.y)
# changing slotted attributes is possible
foo.z = 2

# %%
# overwriting unslotted attributes won't work
foo.foo = lambda:print("Guerilla patching attempt")
# overwriting a accordingly defined property won't work
foo.z = foo.foo
# expanding won't work
foo.x = 1
# %% inheriting won't work
class Bar(Foo):
    pass

在这方面,富不能被继承或在扩大。 缺点是,所有的属性都必须明确地开槽,或仅限于只读类变量。



文章来源: Python's equivalent of .Net's sealed class