Nov-01-2018, 06:32 PM
(This post was last modified: Nov-01-2018, 06:32 PM by Gribouillis.)
In C++, if a class
In python if class
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
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
Output:localhost - localhost - localhost
127.0.0.1 - 127.0.0.1 - 127.0.0.1
0.0.0.0 - 0.0.0.0 - 0.0.0.0
HOST-B - HOST-B - HOST-B
After-b - After-b - After-b
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; }