C++-type static members for python classes - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: General (https://python-forum.io/forum-1.html) +--- Forum: Code sharing (https://python-forum.io/forum-5.html) +--- Thread: C++-type static members for python classes (/thread-13811.html) |
C++-type static members for python classes - Gribouillis - Nov-01-2018 In C++, if a class A has a static string field A::host initialized to "localhost" , the value of this field can be changed through the class by writing for example A::host = "127.0.0.1" or through an instance a of the class, by writing a.host = '127.0.0.1' . Both statements have the same net result.In python if class A has a member A.host and a is an instance of the class, the statement a.host = '127.0.0.1' doesn't change the value of A.host , instead, it updates a member of this particular instance.The following snippet allows one to define fields in a python class having the same behavior as C++ static fields. This is achieved by creating a metaclass on the fly for the target class with a call to function metaclass_with_cpp_fields() below. The descriptor protocol and the metaclass mechanism make this possible. Let us remark that this doesn't prevent class A from using another metaclass by using the parent_metaclass argument of metaclass_with_cpp_fields .class Field: def __init__(self, initializer): self.value = initializer def __get__(self, instance, cls): return self.value def __set__(self, instance, value): self.value = value class MetaField: def __init__(self, desc): self.desc = desc def __get__(self, instance, cls): if instance is None: return self else: return self.desc.__get__(instance, cls) def __set__(self, instance, value): self.desc.__set__(instance, value) def metaclass_with_cpp_fields(fields, parent_metaclass=type): class CppFieldsMeta(parent_metaclass): def __new__(cls, name, parents, attrs): attrs.update(ifield) instance = parent_metaclass.__new__(cls, name, parents, attrs) return instance ifield = {name: Field(value) for name, value in fields.items()} for name, value in ifield.items(): setattr(CppFieldsMeta, name, MetaField(value)) return CppFieldsMeta if __name__ == "__main__": metaA = metaclass_with_cpp_fields(dict( host='localhost', port=80,)) class A(metaclass=metaA): pass class B(A): pass a = A() b = B() print(A.host, '-', a.host, '-', B.host) A.host = '127.0.0.1' print(A.host, '-', a.host, '-', B.host) a.host = '0.0.0.0' print(A.host, '-', a.host, '-', B.host) B.host = 'HOST-B' print(A.host, ' - ', a.host, '-', B.host) b.host = 'After-b' print(A.host, '-', a.host, '-', B.host)The output of this code proves that the fields behave like C++ static fields For comparison, here is the equivalent C++ code with the same output#include <iostream> #include <string> using namespace std; class A { public: static string host; static int port; }; class B : public A{ }; string A::host = string("localhost"); int A::port = 80; int main(){ A a; B b; cout << A::host << " - " << a.host << " - " << B::host << endl; A::host = "127.0.0.1"; cout << A::host << " - " << a.host << " - " << B::host << endl; a.host = "0.0.0.0"; cout << A::host << " - " << a.host << " - " << B::host << endl; B::host = "HOST-B"; cout << A::host << " - " << a.host << " - " << B::host << endl; b.host = "After-b"; cout << A::host << " - " << a.host << " - " << B::host << endl; } |