4

I'm writing a python (3.2+) plugin library and I want to create a function which will create some variables automatically handled from config files.

The use case is as follows (class variable):

class X:
    option(y=0)
    def __init__(self):
        pass

(instance variable):

class Y:
    def __init__(self):
        option(y=0)

the option draft code is as follows:

def option(**kwargs):
    frame   = inspect.stack()[1][0]
    locals_ = frame.f_locals
    if locals_ == frame.f_globals:
        raise SyntaxError('option() can only be used in a class definition')

    if '__module__' in locals_:
        # TODO

    else:
        for name, value in kwargs.items():
            if not name in locals_["self"].__class__.__dict__:
                setattr(locals_["self"].__class__, name, VirtualOption('_'+name, static=False))
            setattr(locals_["self"], '_'+name,value)

I have problem the first case, when option is declared as class variable. Is it possible to somehow get reference to the class in which this function was used (in example to class X)?

2 Answers 2

2

You cannot get a reference to the class, because the class has yet to be created. Your parent frame points a temporary function, whose locals() when it completes will be used as the class body.

As such, all you need to do is add your variables to the parent frame locals, and these will be added to the class when class construction is finished.

Short demo:

>>> def foo():
...     import sys
...     flocals = sys._getframe(1).f_locals
...     flocals['ham'] = 'eggs'
... 
>>> class Bar:
...     foo()
... 
>>> dir(Bar)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__locals__', '__lt__', '__module__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'ham']
>>> Bar.ham
'eggs'
Sign up to request clarification or add additional context in comments.

10 Comments

This is a very sensible answer, but is this really guaranteed to be the way it works by the data model? (The answer may be yes -- This is deeper into the inner workings of python than I need to explore most of the time)
@mgilson: yes, it is. You can check the bytecode of a function constructing a class + the ceval.c loop if you really care, but that's how it works.
I suppose I'm thinking of other python implementations (Jython, pypy ...) where ceval.c may be completely irrelevant ...
@mgilson: it's part of the python specification. The class’s suite is then executed in a new execution frame.
Thank you, I knew about it, but I wanted to get the reference to the class. You have told, that the class is not yet created, so it is impossible. Thank you :)
|
0

It seems to me that a metaclass would be suitable here:

python2.x syntax

def class_maker(name,bases,dict_):
    dict_['y']=0
    return type(name,bases,dict_)

class X(object):
    __metaclass__ = class_maker
    def __init__(self):
        pass

print X.y
foo = X()
print foo.y

python3.x syntax

It seems that python3 uses a metaclass keyword in the class definition:

def class_maker(name,bases,dict_):
    dict_['y']=0
    return type(name,bases,dict_)

class X(metaclass=class_maker):
    def __init__(self):
        pass

print( X.y )
foo = X()
print( foo.y )
print( type(foo) )

Or, more along the lines of what you have in your question:

def class_maker(name,bases,dict_,**kwargs):
    dict_.update(kwargs)
    return type(name,bases,dict_)

class X(metaclass=lambda *args: class_maker(*args,y=0)):
    def __init__(self):
        pass

print( X.y )
foo = X()
print( foo.y )
print( type(foo) )

5 Comments

I don't think that using a metaclass will handle the instance usecase correctly, at least not without wrapping or otherwise hacking at __init__ within the metaclass as well. I was thinking some sort of descriptor might do the trick, especially since he wants to be able to access the class from the calling context.
@sr2222 -- the instance use case seems very convoluted to me. Why not create a base class with an self._option(**kwargs) method and just use setattr in there?
Quite so, though I suspect "why not just" may be a long rabbit hole to travel here.
@sr2222 -- You're probably right about that. Anyway, I don't know much about metaclasses -- I think this is the first time I've ever even made an attempt to answer a question using one ... This is sort of a learning experience for me :)
No, it will not be suitable. As sr2222 told, there are some special conditions why I dont want to use metaclass, and yes, metaclasses in Python3+ are handled in other way (as keyword argument to the class definition)

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.