Equivalent to Python.Net private class

Is there anything like a private class in Python? I believe this is also known as the last class, in Java.

In other words, can we tag a class in Python so that it can never be inherited or extended? Has Python ever thought of having such a function? What for?

Denial of responsibility

Actually trying to understand why closed classes even exist. The answer here (and in many , many , many , many , many , very many other places) did not satisfy me at all, so I try to look from a different perspective. Please avoid theoretical answers to this question and focus on the title! Or, if you insist, at least give one very good and practical example of a sealed class in csharp, indicating that it might break if it is printed.

I am not an expert in both languages, but I know a little of both. Just yesterday, while writing csharp code, I learned about the existence of closed classes. And now I'm wondering if Python has anything like this. I believe that there is a very good reason for its existence, but I really do not understand.

+8
source share
4 answers

You can use metaclass to prevent subclasses:

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 

gives:

 >>> 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 

The string __metaclass__ = Final makes the Foo class "sealed."

Note that you should use a closed class in .NET as a measure of performance; since subclass methods will not be solved directly. Python method lookups work differently, and there is no advantage or disadvantage when it comes to method lookups to using a metaclass, as in the above example.

+12
source

Python has classes that cannot be extended, such as bool or NoneType :

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

However, such classes cannot be created from Python code. (In the CPython C API, they are created without setting the Py_TPFLAGS_BASETYPE flag.)

Python 3.6 will introduce the special __init_subclass__ method; increasing errors due to this will prevent subclassing. For older versions, you can use the metaclass.

However, the most "Putin" way to limit the use of a class is to document how it should not be used.

+1
source

Before talking about Python, let's talk "sealed":

I also heard that the advantage of fully enclosed .Net / Java final / C ++ virtual classes is performance. I heard this from a .Net developer at Microsoft, so maybe this is true. If you are creating a powerful, highly sensitive application or framework, you may need to close several classes at or near the most profiled bottleneck. Especially the classes you use in your own code.

For most software applications, sealing a class that other commands use as part of a framework / library / API looks pretty ... weird.

Mostly because there is a simple workaround for any private class.

I teach Essential Test-Driven Development courses, and in these three languages ​​I suggest users of such a private class wrap it in a delegating proxy server that has exactly the same method signatures, but they can be redefined (virtually), so developers can create test doubles for these slow, non-deterministic, or side-effects of external dependencies.

[Warning: below snark is intended as humor. Please read with activated humorous routines. I understand that there are times when sealed / final are needed.]

A proxy (which is not test code) effectively prints out (re-virtualizes) the class, which leads to searches in v-tables and, possibly, to less efficient code (if the compiler optimizer is not competent enough to embed delegation). The advantages are that you can effectively test your own code, saving living and breathing people weeks of debugging (as opposed to saving your application several million microseconds) per month ... [Disclaimer: it's just a WAG. Yes, I know your application is special. ; -]

So my recommendations are: (1) trust the compiler optimizer, (2) stop creating unnecessary sealed / final / non-virtual classes that you created in order to either (a) use every microsecond of performance in a place that is probably not in any case , your bottleneck (keyboard, Internet ...) or (b) create some kind of improper compilation time limit for the “junior developers” in your team (yes ... I saw that too).

Yes, and (3) write the test first. ;-)

Well, yes, there is always a mockery during the connection (e.g. TypeMock). You caught me. Come on, close your class. Whatevs.

Back to Python. The fact that a keyword is used instead of a hacker probably reflects the purely virtual nature of Python. It is simply not "natural."

By the way, I came to this question because I had exactly the same question. While working on the Python port in my very complex and realistic laboratory of legacy code, I wanted to find out if Python has such a disgusting keyword like sealed or final (I use them in Java, C # and C ++ courses as a unit testing challenge). This is apparently not the case. Now I need to find something as complex in the unverified Python code. Hmmm ...

+1
source

By its purpose, it resembles a sealed class and is useful for reducing memory usage ( Usage of __slots__? ) - the __slots__ attribute that prevents __slots__ fixing the class. Since with __new__ metaclass __new__ is too late to put __slots__ into the class, we must put it in the namespace at the first possible time, that is, during __prepare__ . Also, this raises a TypeError error a bit earlier. Using mcs to compare isinstance eliminates the need for hard coding the metaclass name. The disadvantage is that all unoccupied attributes are read-only. Therefore, if we want to set certain attributes during initialization or later, they must be specially selected. This is possible, for example, using a dynamic metaclass in which slots are used as an argument.

 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") 

where an attempt to overwrite foo.foo will cause AttributeError: 'Foo' object attribute 'foo' is read-only available AttributeError: 'Foo' object attribute 'foo' is read-only and AttributeError: 'Foo' object has no attribute 'x' will be thrown when trying to add foo.x AttributeError: 'Foo' object has no attribute 'x' . The bounding power of __slots__ will be violated by inheritance, but since Foo has a Final metaclass, you cannot inherit from it. It will also be broken when the dict is in the slots , so we add a ValueError just in case. In conclusion, defining setters and getters for properties with slots allows you to limit how the user can overwrite them.

 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 

In this regard, Foo cannot be inherited or extended. The disadvantage is that all attributes must be set explicitly.

0
source

Source: https://habr.com/ru/post/945066/


All Articles