Member Variables in Python



Member variables in python are horrible. They are not visible in the layout of the class which is instantiated, but instead the __init__ function of a class creates certain member variables for the instance. I have never liked this about python, to be honest. For a recent project, I devised the following solution. Assume you would want this behaviour:
>>> class test(Base):
...     # Variables
...     number = 4
...     string = "hodor"
...     # Functions
...     def stringmult(self):
...         return self.number * self.string
...
>>> test().stringmult()
'hodorhodorhodorhodor'
>>> test(number=2).stringmult()
'hodorhodor'
>>> test(string="Na",number=8).stringmult() + " - Batman!"
'NaNaNaNaNaNaNaNa - Batman!'
>>> 
>>> test(end="Batman!")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError
>>>
In other words, any class that inherits from Base can be constructed with keyword arguments who must match exactly the correct class variables which you specify. This one does it:
from ctypes import ArgumentError
class Base(object):
    def __init__(self, **kwargs):
        # "given" is the list of keyword arguments passed to the constructor
        # of this object, "needed" is the list of class variables which belong to  
        # the base class of the object which is being created, which do not end 
        # with two underscores and which are not a function. Trust me, we do not 
        # want to meddle with those.
        given = list(kwargs.keys())
        needed = [attr for attr in dir(self.__class__) if attr[-2:] != '__' \
             and type(self.__class__.__dict__[attr])!=type(lambda:0) ]

        # Check if keyword arguments have been provided which are not among the
        # required arguments and throw an exception if so. Remove this check for
        # a less restrictive base class. I wouldn't recommend it.
        if not set(given) <= set(needed):
            raise ArgumentError()

        # First, initialize the attribute dictionary of the object being created
        # with a list of default values, indicated by the values of the class 
        # variables. Then, update the attribute dictionary again with the values
        # provided to this constructor.
        self.__dict__.update({k: self.__class__.__dict__[k] for k in needed})
        self.__dict__.update(kwargs)
I personally like this approach a lot and hereby dare you to tell me even a single reason not to do this, in the comments.

Leave a Reply

Your email address will not be published.