How to add property to a class dynamically?
Asked Answered
M

26

318

The goal is to create a mock class which behaves like a db resultset.

So for example, if a database query returns, using a dict expression, {'ab':100, 'cd':200}, then I would like to see:

>>> dummy.ab
100

At first I thought maybe I could do it this way:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

but c.ab returns a property object instead.

Replacing the setattr line with k = property(lambda x: vs[i]) is of no use at all.

So what is the right way to create an instance property at runtime?

P.S. I am aware of an alternative presented in How is the __getattribute__ method used?

More answered 25/8, 2009 at 1:53 Comment(5)
There are a few typos in your code: definition of fn_readonly needs a : and __init__ references self.fn_readyonly.Dunham
You are right. I added that setter function in last minute in order to underline the reason of creating a property in runtime.More
The main issue I had with creating properties on initialization was that, in some cases if I called the helpers quickly after, or there was an issue, I'd get an error that they didn't exist despite the fact that they do. In my solution below, I create 2 classes. One as a Base / Parent ( which I am trying to find a solution to avoid ), and the main object, which extends the Base / Parent. Then, in the main object, without initializing, I call my AccessorFunc creator which creates the properties, helper functions, and more.Capricorn
ie: class ExampleBase: pass; class Example( ExampleBase ): __x = Accessor( ExampleBase, 'x', 'X', 123 ); --- which would create a property under x and named functions using X so GetX, SetX, and more... and .x, ._x, and .__x for the property. So .x is the property itself for the data to pass through ( getting / setting via self.x = 123; or self.x to output ). I used self._x for the RAW data stored so it could be easily accessible as I also allowed default values to be assigned, without setting them in the stored data. so _x could be None and .x could return 123. and .__x linked to the AccessorCapricorn
Here is a link to the basic version which creates dynamic properties, and dynamic functions - the file has a bunch of links to other versions. One is the AccessorFunc system using a function to create helpers ( one for functions, one for properties, one for both as individual elements - so it doesn't use code shortening in anything in that file ).. If anything is missing, one of the other files has it: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0Capricorn
E
458

I suppose I should expand this answer, now that I'm older and wiser and know what's going on. Better late than never.

You can add a property to a class dynamically. But that's the catch: you have to add it to the class.

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

A property is actually a simple implementation of a thing called a descriptor. It's an object that provides custom handling for a given attribute, on a given class. Kinda like a way to factor a huge if tree out of __getattribute__.

When I ask for foo.b in the example above, Python sees that the b defined on the class implements the descriptor protocol—which just means it's an object with a __get__, __set__, or __delete__ method. The descriptor claims responsibility for handling that attribute, so Python calls Foo.b.__get__(foo, Foo), and the return value is passed back to you as the value of the attribute. In the case of property, each of these methods just calls the fget, fset, or fdel you passed to the property constructor.

Descriptors are really Python's way of exposing the plumbing of its entire OO implementation. In fact, there's another type of descriptor even more common than property.

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

The humble method is just another kind of descriptor. Its __get__ tacks on the calling instance as the first argument; in effect, it does this:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

Anyway, I suspect this is why descriptors only work on classes: they're a formalization of the stuff that powers classes in the first place. They're even the exception to the rule: you can obviously assign descriptors to a class, and classes are themselves instances of type! In fact, trying to read Foo.bar still calls property.__get__; it's just idiomatic for descriptors to return themselves when accessed as class attributes.

I think it's pretty cool that virtually all of Python's OO system can be expressed in Python. :)

Oh, and I wrote a wordy blog post about descriptors a while back if you're interested.

Erida answered 31/8, 2009 at 1:30 Comment(7)
No need to add the add_property method. setattr (Foo, 'name', property (func))Cohla
Your "But that's the catch…" just saved me several hours of work. Thank you.Mosira
If you want to define a property on a single instance, you can create a class at runtime and modify __class__.Trinitrocresol
what about @myproperty.setter? How to add it dinamically?Ciliary
You do not need to add a property to an initialized object. Doing so may mean it sticks only to the instance but I'd have to double check that. I know I ran into a similar issue where my dynamic properties were instance only, I also ended up with a static setup and the one I wanted which is to the object so future Initializations would make use of them. My post is below and it creates helper functions, and easy ways to easily access everything. .x for property, ._x for the raw data the getter / setter uses ( which can be None ), and .__x for the accessor object.Capricorn
The nice thing about my solution is you can add, on a per initialized accessorfunc, define if it should be restricted to certain data-types, or even values. so a position object with X / Y / Z will only accept numerical values. An Employee with a .name can only accept text. And so on. I am extending so different types will dynamically add new functions such as Has, Add, and so on. But some of those functions will work with any data-type provided the correct magic functions exist.Capricorn
You are missing assigning the property name to the descriptor. You need Foo.b.__set_name__(Foo, 'b'). See object.__set_name__ It is called automatically when the class Foo is declared, so adding a property afterwards misses this call. (New in 3.6)Interlinear
K
71

The goal is to create a mock class which behaves like a db resultset.

So what you want is a dictionary where you can spell a['b'] as a.b?

That's easy:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
Kalin answered 25/8, 2009 at 14:33 Comment(3)
in a more general setup, this serves limited purpose. if the dict has multilevel hierarchy, like d = {'a1': {'b': 'c'}, 'a2': ...}, then while you can do d.a1 or d.a2, you can't do d.a1.bStaphylorrhaphy
One thing to keep in mind is that this allows setting attribute values for attributes with the same name as dict methods or attributes, but does not allow retrieving the values the same way again: d.items = 1, d.items returns <built-in method items of atdict object at ...>. You could still do d["items"] or use __getattribute__ instead of __getattr__, but this prevents using most of the dict's methods.Sendal
Just use the munch library! (fork of bunch)Leatherjacket
B
47

You don't need to use a property for that. Just override __setattr__ to make them read only.

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

Tada.

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
Bolitho answered 25/8, 2009 at 2:41 Comment(0)
E
41

It seems you could solve this problem much more simply with a namedtuple, since you know the entire list of fields ahead of time.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

If you absolutely need to write your own setter, you'll have to do the metaprogramming at the class level; property() doesn't work on instances.

Erida answered 25/8, 2009 at 2:28 Comment(6)
Great idea. Unfortunately I am stuck with python 2.4 at the moment.More
Named tuple recipe adapted for Python 2.4Dichotomy
The guy who wrote namedtuple deserves a prize for making it smooth and elegant to be faithful object-oriented principles.Lindblad
Sorry, at best, this answer is only applicable to the special case where one wanteda class consisting of only read-only attributes all know in advance. In other words I don't think it addresses the broader question of how to add general properties -- not just read-only ones -- to a class at runtime (nor does the current version of the other "add-on" answer also posted by the author).Salters
@Salters so... pass more arguments to property()? there's nothing in either answer that's specific to read-only properties.Erida
Sorry, my mistake, I guess your other answer does address the problem. This one, which suggests initially making the class a namedtuple, doesn't however.Salters
A
16

How to add property to a python class dynamically?

Say you have an object that you want to add a property to. Typically, I want to use properties when I need to begin managing access to an attribute in code that has downstream usage, so that I can maintain a consistent API. Now I will typically add them to the source code where the object is defined, but let's assume you don't have that access, or you need to truly dynamically choose your functions programmatically.

Create a class

Using an example based on the documentation for property, let's create a class of object with a "hidden" attribute and create an instance of it:

class C(object):
    '''basic class'''
    _x = None

o = C()

In Python, we expect there to be one obvious way of doing things. However, in this case, I'm going to show two ways: with decorator notation, and without. First, without decorator notation. This may be more useful for the dynamic assignment of getters, setters, or deleters.

Dynamic (a.k.a. Monkey Patching)

Let's create some for our class:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

And now we assign these to the property. Note that we could choose our functions programmatically here, answering the dynamic question:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

And usage:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

Decorators

We could do the same as we did above with decorator notation, but in this case, we must name the methods all the same name (and I'd recommend keeping it the same as the attribute), so programmatic assignment is not so trivial as it is using the above method:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

And assign the property object with its provisioned setters and deleters to the class:

C.x = x

And usage:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
Anton answered 5/2, 2015 at 23:12 Comment(0)
L
12

Here is a solution that:

  • Allows specifying property names as strings, so they can come from some outside data source instead of all being listed in your program.
  • Adds the properties when the class is defined, instead of every time an object is created.

After the class has been defined, you just do this to add a property to it dynamically:

setattr(SomeClass, 'propertyName', property(getter, setter))

Here is a complete example, tested in Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)
Ligamentous answered 27/9, 2019 at 19:31 Comment(0)
D
9

For those coming from search engines, here are the two things I was looking for when talking about dynamic properties:

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__ is good if you want to put dynamically created properties. __getattr__ is good to only do something when the value is needed, like query a database. The set/get combo is good to simplify the access to data stored in the class (like in the example above).

If you only want one dynamic property, have a look at the property() built-in function.

Drexler answered 10/8, 2016 at 12:58 Comment(0)
A
7

You can use the following code to update class attributes using a dictionary object:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro
Audy answered 27/2, 2018 at 20:29 Comment(2)
This is simple, elegant, straightforward, and exactly what I was looking for. Thanks a ton!Palinode
My favorite for what I needed, easy and to the point.Phonetic
E
6

I asked a similary question on this Stack Overflow post to create a class factory which created simple types. The outcome was this answer which had a working version of the class factory. Here is a snippet of the answer:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

You could use some variation of this to create default values which is your goal (there is also an answer in that question which deals with this).

Expressivity answered 26/8, 2009 at 9:15 Comment(0)
D
5

Not sure if I completely understand the question, but you can modify instance properties at runtime with the built-in __dict__ of your class:

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12
Doralin answered 25/8, 2009 at 2:19 Comment(2)
In essence my question is to find out if it is possible to create a new property in runtime. The consensus seems to be negative. Your suggestion is certainly simple and practical. (Same to other answers that uses dict)More
A simple answer would also be: self.__dict__[key] = valueGnathonic
E
5

You cannot add a new property() to an instance at runtime, because properties are data descriptors. Instead you must dynamically create a new class, or overload __getattribute__ in order to process data descriptors on instances.

Eberle answered 25/8, 2009 at 2:23 Comment(2)
This is wrong. You can add the property to the class then access it from the method.Enjambment
This might actually have been correct for python2, when this answer was written. The behavior is different.Porfirioporgy
H
3

This is a little different than what OP wanted, but I rattled my brain until I got a working solution, so I'm putting here for the next guy/gal

I needed a way to specify dynamic setters and getters.

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

I know my fields ahead of time so im going to create my properties. NOTE: you cannot do this PER instance, these properties will exist on the class!!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

Let's test it all now..

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

Is it confusing? Yes, sorry I couldn't come up with any meaningful real world examples. Also, this is not for the light hearted.

Hindustani answered 31/7, 2019 at 20:32 Comment(2)
If I recall correctly, I did find a way during all of my testing to create a STATIC type property / dynamically added g/setter. I'd have to go through all of my previous - but being able to add something which is shared between all instances is definitely possible. As for creating on a per-instance process... I'm pretty sure you can so that one instance has something another does not. I'd have to verify, but I ran into something like this too ( in my first attempts I made a mistake which caused the functions to be created, but not all of the instances had them because of a flaw )Capricorn
Also, every possible solution is welcome as this is a repo of knowledge. It's also exciting to see the different ways different people create solutions to a problem. My solution does a LOT, you whittled this down to something simpler to share. I did a smaller variant of mine too - it should be somewhere in this topic - and I just realized it isn't the one I posted :-)...Capricorn
H
2

The best way to achieve is by defining __slots__. That way your instances can't have new attributes.

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

That prints 12

    c.ab = 33

That gives: AttributeError: 'C' object has no attribute 'ab'

Hypocorism answered 25/8, 2009 at 3:1 Comment(0)
E
2

Just another example how to achieve desired effect

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

So now we can do stuff like:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
Exorbitance answered 9/5, 2014 at 21:41 Comment(0)
U
1

Although many answers are given, I couldn't find one I am happy with. I figured out my own solution which makes property work for the dynamic case. The source to answer the original question:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
Uralian answered 23/2, 2019 at 5:54 Comment(0)
E
1

If the requirement is to dynamically generate properties based on some instance attribute, then the below code can be useful:

import random  

class Foo:
    def __init__(self, prop_names: List[str], should_property_be_zero: bool = False) -> None:
        self.prop_names = prop_names
        self.should_property_be_zero = should_property_be_zero
        
    def create_properties(self):
        for name in self.prop_names:
            setattr(self.__class__, name, property(fget=lambda self: 0 if self.should_property_be_zero else random.randint(1, 100)))

The important point to note is to use setattr(self.__class__, name, ...) and not setattr(self, name, ...)

Usage Example:

In [261]: prop_names = ['a', 'b']

In [262]: ff = Foo(prop_names=prop_names, should_property_be_zero=False)

In [263]: ff.create_properties()

In [264]: ff.a
Out[264]: 10

In [265]: ff.b
Out[265]: 37

In [266]: ft = Foo(prop_names=prop_names, should_property_be_zero=True)

In [267]: ft.create_properties()

In [268]: ft.a
Out[268]: 0

In [269]: ft.b
Out[269]: 0

Setting the property will raise AttributeError: can't set attribute as expected:

In [270]: ff.a = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-270-5f9cad5b617d> in <module>
----> 1 ff.a = 5

AttributeError: can't set attribute

In [271]: ft.a = 5
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-271-65e7b8e25b67> in <module>
----> 1 ft.a = 5

AttributeError: can't set attribute
Eulalie answered 1/12, 2021 at 12:41 Comment(0)
P
0

This seems to work(but see below):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

If you need more complex behavior, feel free to edit your answer.

edit

The following would probably be more memory-efficient for large datasets:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
Postdoctoral answered 26/8, 2009 at 8:50 Comment(0)
A
0

To answer the main thrust of your question, you want a read-only attribute from a dict as an immutable datasource:

The goal is to create a mock class which behaves like a db resultset.

So for example, if a database query returns, using a dict expression, {'ab':100, 'cd':200}, then I would to see

>>> dummy.ab
100

I'll demonstrate how to use a namedtuple from the collections module to accomplish just this:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

returns 100

Anton answered 6/2, 2015 at 2:4 Comment(0)
S
0
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

And the output is:

>> 1
Servetnick answered 22/11, 2016 at 14:38 Comment(0)
C
0

Extending the idea from kjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

Output:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
Camp answered 13/2, 2019 at 9:56 Comment(0)
A
0

Something that works for me is this:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

Output

a
aa
Altdorf answered 26/5, 2019 at 21:16 Comment(0)
S
0

Elaborated version of the kjfletch's take for kind of objects of an anonimous types.

def construct(*slot_names, type_name: str = None, **initilized_slots):
""" If type_name is specified, creates a type named :type_name: with the specified slot names and values, then returns an instance of it; if no :type_name: is given, constructs an object of the default type.
 'Construct' is used if the :name: is None"""

    class __construct__:
        def __init__(self, *slot_values, **slots):
            self.__dict__.update(initilized_slots)
            for i, name in enumerate(slot_names):
                if i < len(slot_values):
                    setattr(self, name, slot_values[i])
            self.__dict__.update(slots)

        def __repr__(self):
            return '(' + ', '.join(f'{s}={self.__dict__[s]}' for s in initilized_slots.keys()) + ')'

    initilized_slots.update(dict((k, None) for k in slot_names))
    if type_name is None:
        return __construct__(initilized_slots)
    return type(type_name, (__construct__,), {})

Pros:

  • less bloated than namedtuple;
  • linter understands an object to be an instance of a Protocol and gives no warning

Cons:

  • pickling fails
  • no eq etc add it if you want

enter image description here

3

Settera answered 19/10, 2023 at 15:59 Comment(0)
B
-1

I recently ran into a similar problem, the solution that I came up with uses __getattr__ and __setattr__ for the properties that I want it to handle, everything else gets passed on to the originals.

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10
Blackleg answered 29/4, 2014 at 17:52 Comment(2)
I looked into this, however you are technically going through a list in your getter and setter helpers. Because of this, each call would be slower because you're looking it up from a list, first, instead of accessing it directly. Unless Python auto-maps it for you; it may, but I haven't benchmarked this yet to know for sure, but it is a concern for me before I try it. Second, by doing this, you have to define the helpers another way. You also can't lock out data-types and / or values without ending up with a large dictionary, or a lot of extra lines.Capricorn
ie: I'd either have to create a base class which I extend all of my children which use the system from, OR I have to add s/getattr magic functions to everything and duplicate the system every single time. The declaration of properties also means you have to set them up one way, and if you want any of the additional support, as I've listed such as data-type and or value protection to allow or prevent data from being assigned and other helpers, then you have to code them in. Granted, you could make the system similar in behavior but it ends up where you declare slightly differently and bulkier.Capricorn
D
-1

Here is the simple example to create property object programmatically.

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''
Decrepit answered 5/1, 2020 at 20:3 Comment(0)
F
-2

Only way to dynamically attach a property is to create a new class and its instance with your new property.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
Fill answered 25/8, 2009 at 2:29 Comment(5)
this does not appear to work. it will assign the result of the property, not the property itself.Xenomorphic
This is incorrect. I attach dynamic properties with my system without ever having to initialize the class. Initialize being x = Example( ), then adding the property to x.Capricorn
If you look at my code, you'll see that I use class ExampleBase: pass, then class Example( ExampleBase ): ... then I attach the properties to ExampleBase, because the name then exists, and since Example extends from it, it has access to everything. I use the __ var for the accessor helper to be able to have direct access to the accessor objects, I use _ for the data stored ( raw ) which can be None, and no underscores for the actual property which goes through the getter. I can call the getter function using a dynamically added function, or use the property. All without having first init'd.Capricorn
Note: I did mention it - but my definition for definition means the reference exists in the namespace - ie: class Example( Object ): pass... It exists, but hasn't been initialized. Initialization means blah = Example( ); now the object has been 'duplicated' and constructed, then stored as a reference in blah. --- If you do this, then the functions / properties dynamically added should only be in the instance - the issue I had with this was even if the functions existed, there were cases where I got an error saying they didn't. Either blocking error stopped creation, or async execution.Capricorn
A few other notes: It is possible to create properties dynamically, and functions in a way that exist only per instance. You can also make them so they exist for the object ( which is what you'd want in most cases ). And there are cases in which the added elements are 'static', ie the same references, and returned values are shared across all instances - if you update in one area, all get the same..Capricorn
C
-5

A lot of the supplied answers require so many lines per property, ie / and / or - what I'd consider an ugly or tedious implementation because of repetitiveness required for multiple properties, etc. I prefer keeping boiling things down / simplifying them until they can't be simplified anymore or until it doesn't serve much purpose to do so.

In short: in completed works, if I repeat 2 lines of code, I typically convert it into a single line helper function, and so on... I simplify math or odd arguments such as ( start_x, start_y, end_x, end_y ) to ( x, y, w, h ) ie x, y, x + w, y + h ( sometimes requiring min / max or if w / h are negative and the implementation doesn't like it, I'll subtract from x / y and abs w / h. etc.. ).

Overriding the internal getters / setters is an ok way to go, but the problem is you need to do that for every class, or parent the class to that base... This doesn't work for me as I'd prefer to be free to choose the children / parents for inheritance, child nodes, etc.

I have created a solution which answers the question without using a Dict data-type to supply the data as I find that to be tedious to enter the data, etc...

My solution requires you to add 2 extra lines above your class to create a base class for the class you want to add the properties to, then 1 line per and you have the option to add callbacks to control the data, inform you when data changes, restrict the data which can be set based on value and / or data-type, and much more.

You also have the option to use _object.x, _object.x = value, _object.GetX( ), _object.SetX( value ) and they are handled equivalently.

Additionally, the values are the only non-static data which are assigned to the class instance, but the actual property is assigned to the class meaning the things you don't want to repeat, don't need to be repeated... You can assign a default value so the getter doesn't need it each time, although there is an option to override the default default value, and there is another option so the getter returns the raw stored value by overriding default returns ( note: this method means the raw value is only assigned when a value is assigned, otherwise it is None - when the value is Reset, then it assigns None, etc.. )

There are many helper functions too - the first property which gets added adds 2 or so helpers to the class for referencing the instance values... They are ResetAccessors( _key, .. ) varargs repeated ( all can be repeated using the first named args ) and SetAccessors( _key, _value ) with the option of more being added to the main class to aide in efficiency - the ones planned are: a way to group accessors together, so if you tend to reset a few at a time, every time, you can assign them to a group and reset the group instead of repeating the named keys each time, and more.

The instance / raw stored value is stored at class., the __class. references the Accessor Class which holds static vars / values / functions for the property. _class. is the property itself which is called when accessed via the instance class during setting / getting, etc.

The Accessor _class.__ points to the class, but because it is internal it needs to be assigned in the class which is why I opted to use __Name = AccessorFunc( ... ) to assign it, a single line per property with many optional arguments to use ( using keyed varargs because they're easier and more efficient to identify and maintain )...

I also create a lot of functions, as mentioned, some of which use accessor function information so it doesn't need to be called ( as it is a bit inconvenient at the moment - right now you need to use _class..FunctionName( _class_instance, args ) - I got around using the stack / trace to grab the instance reference to grab the value by adding the functions which either run this bit marathon, or by adding the accessors to the object and using self ( named this to point out they're for the instance and to retain access to self, the AccessorFunc class reference, and other information from within the function definitions ).

It isn't quite done, but it is a fantastic foot-hold. Note: If you do not use __Name = AccessorFunc( ... ) to create the properties, you won't have access to the __ key even though I define it within the init function. If you do, then there are no issues.

Also: Note that Name and Key are different... Name is 'formal', used in Function Name Creation, and the key is for data storage and access. ie _class.x where lowercase x is key, the name would be uppercase X so that GetX( ) is the function instead of Getx( ) which looks a little odd. this allows self.x to work and look appropriate, but also allow GetX( ) and look appropriate.

I have an example class set up with key / name identical, and different to show. a lot of helper functions created in order to output the data ( Note: Not all of this is complete ) so you can see what is going on.

The current list of functions using key: x, name: X outputs as:

This is by no means a comprehensive list - there are a few which haven't made it on this at the time of posting...

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

Some of the data being output is:

This is for a brand new class created using the Demo class without any data assigned other than the name ( so it can be output ) which is _foo, the variable name I used...

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

And this is after assigning all of _foo properties ( except the name ) the following values in the same order: 'string ', 1.0, True, 9, 10, False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

Note that because of restricted data-types or value restrictions, some data wasn't assigned - this is by design. The setter prohibits bad data-types or values from being assigned, even from being assigned as a default value ( unless you override the default value protection behavior )

The code hasn't been posted here because I didn't have room after the examples and explanations... Also because it will change.

Please Note: at the time of this posting, the file is messy - this will change. But, if you run it in Sublime Text and compile it, or run it from Python, it will compile and spit out a ton of information - the AccessorDB portion isn't done ( which will be used to update the Print Getters and GetKeyOutput helper functions along with being changed to an Instance function, probably put into a single function and renamed - look for it.. )

Next: Not everything is required for it to run - a lot of the commented stuff at the bottom is for more information used for debugging - it may not be there when you download it. If it is, you should be able to uncomment and recompile to get more information.

I am looking for a work-around to needing MyClassBase: pass, MyClass( MyClassBase ): ... - if you know of a solution - post it.

The only thing necessary in the class are the __ lines - the str is for debugging as is the init - they can be removed from the Demo Class but you will need to comment out or remove some of the lines below ( _foo / 2 / 3 )..

The String, Dict, and Util classes at the top are a part of my Python library - they are not complete. I copied over a few things I needed from the library, and I created a few new ones. The full code will link to the complete library and will include it along with providing updated calls and removing the code ( actually, the only code left will be the Demo Class and the print statements - the AccessorFunc system will be moved to the library )...

Part of file:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

This beauty makes it incredibly easy to create new classes with dynamically added properties with AccessorFuncs / callbacks / data-type / value enforcement, etc.

For now, the link is at ( This link should reflect changes to the document. ): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Also: If you don't use Sublime Text, I recommend it over Notepad++, Atom, Visual Code, and others because of proper threading implementations making it much, much faster to use... I am also working on an IDE-like code mapping system for it - take a look at: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ ( Add Repo in Package Manager first, then Install Plugin - when version 1.0.0 is ready, I'll add it to the main plugin list... )

I hope this solution helps... and, as always:

Just because it works, doesn't make it right - Josh 'Acecool' Moser

Capricorn answered 31/8, 2018 at 8:20 Comment(2)
I wanted to add a quick display of what the class looked like so you don't need to open the code file but comments don't appear to support it..Capricorn
Apparently this is getting a lot of hate, which is confusing. It does exactly what the OP is asking for - dynamically adding properties to an object. It also adds helper functions, which don't have to be included - maybe that's why it is getting hate - and it also makes sure the developer has an easy way to access the property ( .x ) which is processed through the getter, the raw value stored ( ._x ) which can be None when .x returns the default or something else, and a way to access the accessor to use helpers, change things, etc.. ( .__x )....Capricorn

© 2022 - 2024 — McMap. All rights reserved.