📜 ⬆️ ⬇️

Variable properties of classes in python: benefits for business and disorderly conduct

In Python, class attributes can be modified as much as you like while working, and changes are visible to all objects of this class and other subclasses. Under the cut - one useful application of this fact.


Generally speaking, little in python cannot be changed on the move by simple assignment. There is a small list of reserved names that really cannot be assigned anything under the fear of SyntaxError, but it is really small:


>>> import keyword >>> keyword.kwlist ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield'] 

Everything else, including the builtins namespace, is just variables, mostly standard types, which can be reassigned at any time. Very convenient, because it allows you to easily slip the interpreter the desired function or value instead of the built-in.


The same applies (with reservation, see below) to the attributes of classes, due to which you can transfer data at once to a heap of objects without a cumbersome message system. Suppose we have a certain singleton and a bunch of objects that refer to it. Singletons of this type, generally speaking, can be several; not at the same time, of course, but sooner or later it may turn out that the current singleton should be thrown out and replaced with a new one. The situation is quite real: in my case it was a game in which all entities on the screen sometimes transmit information to an object that monitors the statistics of the game as a whole. And yes, I know about the event queue and other things, but in the case of the quick-and-dirty prototype, the solution (and speed, by the way) is sometimes better than the right architecture.


 class A(): some_variable = None # The rest of the class class B(A): # Class B code b = B() A.some_variable = 'foo' assert b.some_variable == 'foo' 

This simple test is passed regardless of how many levels of inheritance are between A and B (provided that some_variable is not overridden by any of the heirs of A) and the classes are scattered around the files. Of course, this method has pitfalls. The main thing is that classes almost never die. That is, if some_variable is a heavy object with a lot of data, then even after deleting all objects A and its subclasses, the garbage collector will not touch it. The responsibility for removing A.some_variable lies solely with the programmer. Check that the appropriate type of object is assigned is also rather nontrivial. And in general, such a non-standard hack requires detailed documentation, because objects A () do not seem to receive anything from anywhere, and nevertheless from somewhere in the course.


With built-in classes, so to do, alas, is impossible. And it is clear why: if such a hack were possible, half of the pip modules would contain some kind of attack in some unexpected place like this:


 >>> str.format = send_all_your_data_to_me Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't set attributes of built-in/extension type 'str' 

It is still possible to replace the value of the variable __builtins__.str with your class, but this only applies if the string constructor is called explicitly:


 >>> __builtins__.str = None >>> type('') <class 'str'> >>> str(123) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'NoneType' object is not callable >>> a = lambda x: str(x) >>> a(123) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <lambda> TypeError: 'NoneType' object is not callable 

')

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


All Articles