简体   繁体   中英

how to do 'exec' definition inside class Python

guys. I started to study classes recently and while playing around was trying to do something like this :

from functools import partial

class test():
    def __init__(self):
         exec('def foo(self, number): x = number; return x') 

    def printingX(self):
         command = partial(self.foo, 1)
         print command
a = test() 
a.printingX()

It's of course giving an error, but my question is : Is it possible to store method inside ' init ' and then to call it in other methods later? If it's possible, is it a good practice to do or bad?

You can, but generally, don't.

I would say be careful with exec . But other than that, there is not much stopping you really from doing this

>>> class MyClass:
...     def __init__(self):
...         exec('MyClass.foo = lambda self, x: x*x')
...
>>> c = MyClass()
>>> c.foo(3)
9

ALTHOUGH, my suggestion is that you do not do this since it is highly impractical and there is no convention for it.

Is it possible to store method inside 'init'

Depends on what you mean by "storing" ? But if you meant "define", yes, you can define functions (nb: def always defines a function, it only becomes a "method" when it's looked up on a class or instance). And you don't need exec for this:

class Foo(object):
    def __init__(self, arg):
        def bar(baaz):
            print("in bar: {}".format(baaz))
        bar(arg)

and then to call it in other methods later?

If you want to make it a method, you can add it either to the instance:

class Foo(object):
    def __init__(self, arg):
        self.arg = arg
        def bar(self):
            print("in self.bar: {}".format(self.arg))
        self.bar = bar.__get__(self, type(self))

or to the class (which means it will be overwritten every time you instanciate the class):

class Foo(object):
    def __init__(self, arg):
        self.arg = arg
        def bar(self):
            print("in self.bar: {}".format(self.arg))
        type(self).bar = bar

If it's possible, is it a good practice to do or bad?

Well, as written above, it's absurd, confusing and inefficient, so I fail to see how this could qualify as good practice. And doing it with exec is just a complete WTF.

If the goal is to somehow parameterize a given method for each instance (based on a condition or whatnot), you can just pass the function - or any callable FWIW - as an argument (python functions are objects like any other):

class Foo(object):
    def __init__(self, arg, strategy):
        self.arg = arg
        # we may not want to expose it
        # as part of the class API
        self._strategy = strategy

    def bar(self, value):
        return self._strategy(self, value)


def my_strategy(foo, value):
    return foo.arg * value

f = Foo(42, my_strategy)
f.bar(2)

FWIW, this is known as the "strategy" design pattern.

You can do exactly what you wanted, but you must use the globals and locals parameters of exec to limit the risk of exec-ing a dangerous string and getting the function definition:

class test:
    def __init__(self):
        funcs = {}
        exec('def foo(self, number): x = number; return x',
             {'__builtins__':{}}, funcs)
        for func in funcs:
            if not hasattr(self.__class__, func):
                setattr(self.__class__, func, funcs[func])

This code explicitely adds the compiled function into the class on creation of first object.

Demo:

>>> test.__dict__
mappingproxy({'__module__': '__main__', '__init__': <function test.__init__ at 0x000001E5C5F27EA0>, '__dict__': <attribute '__dict__' of 'test' objects>, '__weakref__': <attribute '__weakref__' of 'test' objects>, '__doc__': None})
>>> t = test()
>>> test.__dict__
mappingproxy({'__module__': '__main__', '__init__': <function test.__init__ at 0x000001E5C5F27EA0>, '__dict__': <attribute '__dict__' of 'test' objects>, '__weakref__': <attribute '__weakref__' of 'test' objects>, '__doc__': None, 'foo': <function foo at 0x000001E5C5F27F28>})
>>> t.foo(12)
12

This clearly shows that the foo method has been added to the class when creating its first instance.

But... please don't!!! . This code only shows that Python is kind enough to allow run time modification of classes, and that a method if just a class attribute that happens to be a function. Ok, the string passed to exec is controlled so there is no security problem here, but exec and eval should be avoided when possible.

And unless you have a real need for code like that, it leads to a hard to understand class when readability should be the first thought for a Python programmer.

So to answer your second question it is really a terrible practice. Never say that I advised to use it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM