The mapping obj.method ({argument: value}) to obj.argument (value)

I don't know if that will make sense, but ...

I am trying to dynamically assign methods to an object.

#translate this object.key(value) #into this object.method({key:value}) 

To be more specific in my example, I have an object (which I did not write), let's call it an engine that has several common methods, status, and several others. Some take a dictionary as an argument, and some take a list. To change the engine speed and see the result, I use:

 motor.set({'move_at':10}) print motor.status('velocity') 

The engine object then formats this request into a JSON-RPC string and sends it to the IO daemon. The author of the python object does not care what the arguments are, it just handles formatting and JSON sockets. The lines move_at and velocity are just two of the possible arguments.

Instead, I would like to do the following:

 motor.move_at(10) print motor.velocity() 

I would like to do this in a general way, because I have so many different arguments that I can pass. I do not want to do this:

 # create a new function for every possible argument def move_at(self,x) return self.set({'move_at':x}) def velocity(self) return self.status('velocity') #and a hundred more... 

I did a few searches on this subject, which suggested that the solution lies with lambdas and meta-programming, two subjects with which I could not tear myself away.

UPDATE:

Based on user470379 user code, I got the following ...

 # This is what I have now.... class Motor(object): def set(self,a_dict): print "Setting a value", a_dict def status(self,a_list): print "requesting the status of", a_list return 10 # Now to extend it.... class MyMotor(Motor): def __getattr__(self,name): def special_fn(*value): # What we return depends on how many arguments there are. if len(value) == 0: return self.status((name)) if len(value) == 1: return self.set({name:value[0]}) return special_fn def __setattr__(self,attr,value): # This is based on some other answers self.set({attr:value}) x = MyMotor() x.move_at = 20 # Uses __setattr__ x.move_at(10) # May remove this style from __getattr__ to simplify code. print x.velocity() 

output:

 Setting a value {'move_at': 20} Setting a value {'move_at': 10} 10 

Thanks to everyone who helped!

+4
source share
4 answers

How to create your own __getattr__ for a class that returns a function created on the fly? IIRC, there are several difficult cases to keep track of __getattr__ and __getattribute__ , which I don’t remember from my head, I'm sure someone will post a comment to remind me:

 def __getattr__(self, name): def set_fn(self, value): return self.set({name:value}) return set_fn 

Then it should happen that calling an attribute that does not exist (i.e.: move_at ) will call the __getattr__ function and create a new function that will be returned ( set_fn above). The name variable of this function will be bound to the name parameter passed to __getattr__ ( "move_at" in this case). Then this new function will be called with the arguments you passed ( 10 in this case).

Edit

Shorter version using lambdas (untested):

 def __getattr__(self, name): return lambda value: self.set({name:value}) 
+4
source

There are many different potential answers to this, but many of them are likely to include subclassing the object and / or writing or overriding the __getattr__ function.

Essentially, the __getattr__ function __getattr__ called whenever python cannot find the attribute in the usual way.

Assuming you can subclass your object, here is a simple example of what you can do (this is a bit awkward, but this is the beginning):

 class foo(object): def __init__(self): print "initting " + repr(self) self.a = 5 def meth(self): print self.a class newfoo(foo): def __init__(self): super(newfoo, self).__init__() def meth2(): # Or, use a lambda: ... print "meth2: " + str(self.a) # but you don't have to self.methdict = { "meth2":meth2 } def __getattr__(self, name): return self.methdict[name] f = foo() g = newfoo() f.meth() g.meth() g.meth2() 

Output:

 initting <__main__.foo object at 0xb7701e4c> initting <__main__.newfoo object at 0xb7701e8c> 5 5 meth2: 5 
+1
source

It seems that you have certain β€œproperties” of your object that can be set using

 obj.set({"name": value}) 

and requested

 obj.status("name") 

The general way to switch to Python is to match this behavior with what looks like simple attribute access. Therefore we write

 obj.name = value 

to set the property and we just use

 obj.name 

to request it. This can be easily implemented using __getattr__() and __setattr__() special methods:

 class MyMotor(Motor): def __init__(self, *args, **kw): self._init_flag = True Motor.__init__(self, *args, **kw) self._init_flag = False def __getattr__(self, name): return self.status(name) def __setattr__(self, name, value): if self._init_flag or hasattr(self, name): return Motor.__setattr__(self, name, value) return self.set({name: value}) 

Note that this code prevents the dynamic creation of new β€œreal” attributes of Motor instances after initialization. If necessary, appropriate exceptions can be added to the __setattr__() implementation.

+1
source

Instead of setting the call function using the syntax, consider using an assignment (c =). Similarly, simply use the attribute syntax to get the value instead of the call function syntax. Then you can use __getattr__ and __setattr__ :

 class OtherType(object): # this is the one you didn't write # dummy implementations for the example: def set(self, D): print "setting", D def status(self, key): return "<value of %s>" % key class Blah(object): def __init__(self, parent): object.__setattr__(self, "_parent", parent) def __getattr__(self, attr): return self._parent.status(attr) def __setattr__(self, attr, value): self._parent.set({attr: value}) obj = Blah(OtherType()) obj.velocity = 42 # prints setting {'velocity': 42} print obj.velocity # prints <value of velocity> 
+1
source

Source: https://habr.com/ru/post/1337371/


All Articles