0
votes

I have a handful of objects that hold constant database values for the application, which look like something this:

class TestObject(object):
    """reflects constants in _test_object"""
    _pairs = (
        ( 1       , 'A' ),         
        ( 2       , 'B' ),         
        ( 4       , 'C' ),         
        ( 8       , 'D' ),          
    )
    mapping = setup(_pairs)

The mapping function creates a dict that gives me a k,v and v,k lookup based on the _pairs. They're all in the form of "id,name".

In order to make things easier in the app, I often set the "text values" as an attribute as well:

class TestObject(object):
    ...
    ...
    A = 1
    B = 2
    C = 4
    D = 8

While this makes using the classes really great, it is a bit unwieldy to retype this all, and keep it in sync. Plus I have to write unit-tests to ensure the attributes match up with the right ids in the _pairs.

I've been trying to wrap my head around a way to skip writing this out, and just automatically populate each TestObject class with the attributes on the application startup.

I know I could override the class and have it check the _pairs/mapping attribute for a failover, but I'm really interested in whether or not it's even possible to programmatically populate the class as I need. There is no __class__ yet, because the class hasn't been defined yet.

just to be clear, the type of stuff i want to do is along the lines of this:

class NotValidPython():
    _pairs = ((1,'A'),)
    for (k,v) in _pairs:
        setattr( __class__, v, k )
3

3 Answers

3
votes

Using a decorator:

def dynamic_attrs(cls):
    for v, k in getattr(cls, '_pairs', []):
        setattr(cls, k, v)
    # you also optionally delete the _pairs attr here using del cls._pairs
    return cls

@dynamic_attrs
class TestObject(object):
    """reflects constants in _test_object"""
    _pairs = (
        ( 1       , 'A' ),
        ( 2       , 'B' ),
        ( 4       , 'C' ),
        ( 8       , 'D' ),
    )
1
votes

A metaclass can be used for this. You can either override __new__ in the metaclass and then populate the attribute dictionary, or override __init__ and then override the attribute dictionary or set the attribute on class object itself using setattr.

Using __new__:

class Meta(type):

    def __new__(meta, clsname, bases, dct):
        dct.update(dict((k, v) for v, k in dct.pop('_pairs')))
        return type.__new__(meta, clsname, bases, dct)

class TestObject(object):
    __metaclass__ = Meta

    """reflects constants in _test_object"""
    _pairs = (
        ( 1       , 'A' ),         
        ( 2       , 'B' ),         
        ( 4       , 'C' ),         
        ( 8       , 'D' ),          
    )

Using __init__:

class Meta(type):

    def __init__(cls, clsname, bases, dct):
        attrs = dct.pop('_pairs')
        for v, k in attrs:
            setattr(cls, k, v)
0
votes

No need to use a metaclass. Just put your code in the same module as the class definition, but outside the class definition:

class TestClass():
    _pairs = ((1,'A'),)

for (k, v) in TestClass._pairs:
    setattr(TestClass, v, k)