6

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 :-)

4
  • lets say that I have the following: Commented Sep 5, 2019 at 18:29
  • I have done some experiments with using type() in a factory context, In particular, for establishing local, instance attributes using init. In the case of interest however, the content requires computation I understand that are ways to do this with a function interface python print ('computed content {}'.format(my_instance.getComputed() )) But I would still like access to be of the form: python print ('computed content {}'.format(my_instance.Computed)) Commented Sep 5, 2019 at 18:43
  • sorry about the formatting -- first time I have posted and learning to navigate the API . . . Commented Sep 5, 2019 at 18:44
  • Well, that's exactly what property is for, or you can implement your own custom descriptor object if property doesn't work for you... Commented Sep 5, 2019 at 19:01

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

8 Comments

ah -- this looks promising -- now I see how to setup the property() relationship in the type() call !
is there a way to use something like slots to guard against unexpected attribute reference? I.e., I want to guard against thing like ``` my_instance.NOT_SUPPORTED_PROPERTY ```
@Tomie yeah, just use __slots__.
@Tomie although, Python will always throw an error if you try to access an attribute that doesn't exist. __slots__ will guard against common typo bugs like instance.som_var = 42 when you meant instance.some_var = 42
if I assign __slots__ based on the prop names, I get ValueError: 'A" in __slots__ conflicts with class variable when type() is invoked
|
0

In python, instance variables are stored under a dict which can be used to create dynamic instance variables at the time of object creation of a class.

Let's say, I have a dict {'a':1} in which the key 'a' should be assigned as a variable and 1 as its value.

Under__init__ function, we can do like,

self.__dict__['a'] = 1 

We can use a for loop to iterate over the dictionary and do the above to create dynamic variables.

As per @chepner suggestion, we can also use setattr function to set variables dynamically.

2 Comments

setattr(self, 'a', 1) would be better. Don't use the dunder names directly if you don't have to.
Thanks for the info. Have edited the answer properly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.