Python Forum
C++-type static members for python classes
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
C++-type static members for python classes
#1
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
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;
}
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020