Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

creating class properties dynamically

I am looking for a way to dynamically create classes with specific properties accessible via typical instance notation.

       DynoOne = createClass('DynoOne',props=['A','B'])
       d = DynoOne (database='XYZ')
       d.A = d.B + 1

       DynoTwo = createClass('DynoTwo',props=['A','C','E'])
       q = DynoTwo (database='QRS')
       q.A = q.C + 2*q.E

Details of how the "props" are actually acquired and modified would be hidden. This also makes it easier to add access to new props as they become available.

I have experimented with techniques such as the following, to get a feel for how python can dynamically produce basic class attributes:

Class factory to produce simple struct-like classes?

My initial reading on python suggests that class properties are one way to handle introducing getter/setter methods for access.

What's not clear is how to dynamically specify property names in the factory constructor method (whether using decorators or explicit property() call)

E.g., using property() . . .

   class DynamicClass( someBase ):

       def dynamic_getter(self):
           # acquire "stuff"
           return stuff

       def dynamic_setter(self,stuff):
           # store  "stuff"
           pass 

       dynamic_property_name = property(fget=dynamic_getter,fset=dynamic_setter)

When the class is declared/constructed, I need to create a set per requested prop. E.g., the DynoOne class would have separate property/setter/getter for 'A' and 'B'.

I suspect that a template-based eval() strategy would work, but I am likely missing some more fundamental and effective technique.

Enlightenment and learning opportunities are appreciated :-)

like image 710
Tomie Avatar asked Sep 05 '19 18:09

Tomie


Video Answer


1 Answers

The three-argument for of type lets you create classes dynamically. So, a sketch:

def dynamic_getter(self):
    # acquire "stuff"
    return stuff

def dynamic_setter(self,stuff):
    # store  "stuff"
    pass 

DynamicClass =  type('DynamicClass', (SomeBase,), {"dynamic_property_name":property(fget=dynamic_getter,fset=dynamic_setter)})

Or, more concretely:

In [1]: class SomeBase:
   ...:     def __init__(self):
   ...:         self._foo = 42
   ...:
   ...: def dynamic_getter(self):
   ...:     # acquire "stuff"
   ...:     return self._foo
   ...:
   ...: def dynamic_setter(self,stuff):
   ...:     # store  "stuff"
   ...:     pass
   ...:
   ...: DynamicClass =  type('DynamicClass', (SomeBase,), {"dynamic_property_name":property(fget=dynamic_getter,fset=dynamic_setter)})

In [2]: instance = DynamicClass()

In [3]: instance.dynamic_property_name
Out[3]: 42

Note: type is literally a class object like any other, and calling it in it's three-argument form is a constructor for new class object instances, it is the class that creates other class objects, i.e. a metaclass. Indeed, you can think of a class definition statement as syntactic sugar for the above.

A template-based with exec (if you want to use a complex statement, you'd need exec, eval only allows expressions) approach is also viable, if you find that easier to work with. Indeed, that is how collections.namedtuple works in the standard library.

Note: you seem to be confused about the nature of properties in Python. Instance attribtus are not specified on the class, rather, you'd add a function that initializes those instance attributes (typically __init__) but you can add instance attributes anywhere, even outside of a method/class.

like image 95
juanpa.arrivillaga Avatar answered Oct 19 '22 23:10

juanpa.arrivillaga