Inheritance of function from class but editing that function - python

I'm using Python and I have two classes. I want to import a function for a class but with the ability of adding things to that function.
class Main(self):
def __init__(self):
thingstodo()
def function(self, keypressed):
#LOTS OF CODE
keyname = keypressed
if keyname = "Escape":
dosomething()
class Main2(Main):
def __init(self):
Main.__init__(self)
def function(self, keypressed):
Main.function(self, keypressed)
if keyname = "control":
dootherthing()

Basic principles
You cannot access local variables from one function (or method) in another function. This is by design.
This class Main(self): is wrong. In Python 3 do class Main:. While using self as the name of the first argument in method is a strong convention, self is just an ordinary name not a reserved keyword or built-in.
There are several problems here:
def __init(self):
Main.__init__(self)
a. The method name needs to __init__() not __init.
b. Don't hardwire the name of the parent class with Main.__init__(self) use super().__init__().
c. If you don't do anything extra in the __init__() of Main2, than you don't need to implement the __init__() at all.
Possible solution
For your problem, using a dictionary with the key press names as keys and the functions for the actions as values seems useful.
First define a few small helper functions:
def thingstodo():
print('thingstodo')
def dosomething():
print('something')
def dootherthing():
print('dootherthing')
Now your main class:
class KeyAction: # Python 3
def __init__(self):
thingstodo()
self.key_actions = {'Escape': dosomething}
def handel_key_press(self, keypressed):
#LOTS OF CODE
keyname = keypressed
func = self.key_actions.get(keyname)
if func is not None:
func()
Names are important, therefore I use KeyAction instead of Main.
This line self.key_actions = {'Escape': dosomething} is the core of this solution. Here self.key_actions is a dictionary that maps names of key press events to functions. Note dosomething without the () because I put the function object into the dictionary rather than calling this function.
Calling this function is a bit different:
func = self.key_actions.get(keyname)
if func is not None:
func()
I use the get() method of the dictionary. This returns the value for the key if the key is in it and None if not. Now func holds either a reference to the function dosomething if the key was Escape or None. If it is a function I call it with func().
An alternative here could be a try-except:
def handel_key_press(self, keypressed):
#LOTS OF CODE
keyname = keypressed
try:
self.key_actions[keyname]()
except KeyError:
pass
Now, in your child class, you only need to add another key-value pair to self.key_actions to extend its functionality:
class ExtendedKeyAction(KeyAction):
def __init__(self):
super().__init__()
self.key_actions['control'] = dootherthing
Make two instances and test your code:
key_action = KeyAction()
key_action.handel_key_press('Escape')
key_action.handel_key_press('undefined')
extended_key_action = ExtendedKeyAction()
extended_key_action.handel_key_press('Escape')
extended_key_action.handel_key_press('control')
extended_key_action.handel_key_press('undefined')
prints:
thingstodo
something
thingstodo
something
dootherthing

Related

define a decorator as method inside class

I'm trying to create a method inside my class that counts the complete run of a specific function. I want to use a simple decorator. I found this reference and rewrite this simple script:
class myclass:
def __init__(self):
self.cnt = 0
def counter(function):
"""
this method counts the runtime of a function
"""
def wrapper(self, **args):
function(**args)
self.counter += 1
return wrapper
#myclass.counter
def somefunc():
print("hello from somefunc")
if __name__ == "__main__":
obj = myclass()
# or if comment #myclass.counter
# somefunc = myclass.counter(somefunc)
somefunc()
And of course, I get:
TypeError: wrapper() missing 1 required positional argument: 'self'
I tried to rewrite the counter as a class method:
class myclass:
def __init__(self):
self.cnt = 0
def counter(self, function):
"""
this method counts the runtime of a function
"""
def wrapper(**args):
function(**args)
self.cnt += 1
return wrapper
def somefunc():
print("hello from somefunc")
if __name__ == "__main__":
obj = myclass()
somefunc = obj.counter(somefunc)
for i in range(10):
somefunc()
print(obj.cnt)
Which works fine but I think it is not a valid decorator definition. Is there any way to define the decorator inside the class method and pass the self-argument to its function? or defining a decorator inside a class is useless?
EDIT:------
First I can't define the decoration outside of the class method. Second I'm trying to make a scheduled class that runs a specific function (as input) for a fixed interval and a specific amount of time so I need to count it.
So I was able to draft up something for you, below is the code:
def count(func):
def wrapper(self):
TestClass.call_count += 1
func(self)
return wrapper
class TestClass(object):
call_count = 0
#count
def hello(self):
return 'hello'
if __name__ == '__main__':
x = TestClass()
for i in range(10):
x.hello()
print(TestClass.call_count)
Why would it cause problems to have the decorator in a class:
It's not straight forward to have a decorator function inside the class. The reasons are below:
Reason 1
Every class method must take an argument self which is the instance of the class through which the function is being called. Now if you make the decorator function take a self argument, the decorator call #count would fail as it get converted to count() which doesn't pass the self argument and hence the error:
TypeError: wrapper() missing 1 required positional argument: 'self'
Reason 2
Now to avoid that you can make your decorator as static by changing the declaration like below:
#staticmethod
def count(func):
pass
But then you have another error:
TypeError: 'staticmethod' object is not callable
Which means you can't have a static method as well. If you can't have a static method in a class, you have to pass the self instance to the method but if you pass the self instance to it, the #count decorator call wouldn't pass the self instance and hence it won't work.
So here is a blog that explains it quite well, the issues associated with it and what are the alternatives.
I personally prefer the option to have a helper class to hold all my decorators that can be used instead of the only class in which it's defined. This would give you the flexibility to reuse the decorators instead of redefining them which would follow the ideology
code once, reuse over and over again.
Your second code example is functionally equivalent to a standard decorator. The standard decorator syntax is just a shorthand for the same thing, namely, reassigning a function value equal to a closure (a function pointer with arguments predefined), where the closure is your decorator wrapper holding the original as its predefined argument.
Here's an equivalent with standard syntax. Notice you need to create the counter class instance in advance. The decorator syntax refers to that instance, because it must indicate the specific object which holds your counter, rather than just the class of the object:
class myclass:
def __init__(self):
self.cnt = 0
def counter(self,function):
"""
this method counts the number of runtime of a function
"""
def wrapper(**args):
function(self,**args)
self.cnt += 1
return wrapper
global counter_object
counter_object = myclass()
#counter_object.counter
def somefunc(self):
print("hello from somefunc")
if __name__ == "__main__":
for i in range(10):
somefunc()
print(counter_object.cnt)

More efficient way of setting default method argument to instance attribute [duplicate]

I want to pass a default argument to an instance method using the value of an attribute of the instance:
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=self.format):
print(formatting)
When trying that, I get the following error message:
NameError: name 'self' is not defined
I want the method to behave like this:
C("abc").process() # prints "abc"
C("abc").process("xyz") # prints "xyz"
What is the problem here, why does this not work? And how could I make this work?
You can't really define this as the default value, since the default value is evaluated when the method is defined which is before any instances exist. The usual pattern is to do something like this instead:
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=None):
if formatting is None:
formatting = self.format
print(formatting)
self.format will only be used if formatting is None.
To demonstrate the point of how default values work, see this example:
def mk_default():
print("mk_default has been called!")
def myfun(foo=mk_default()):
print("myfun has been called.")
print("about to test functions")
myfun("testing")
myfun("testing again")
And the output here:
mk_default has been called!
about to test functions
myfun has been called.
myfun has been called.
Notice how mk_default was called only once, and that happened before the function was ever called!
In Python, the name self is not special. It's just a convention for the parameter name, which is why there is a self parameter in __init__. (Actually, __init__ is not very special either, and in particular it does not actually create the object... that's a longer story)
C("abc").process() creates a C instance, looks up the process method in the C class, and calls that method with the C instance as the first parameter. So it will end up in the self parameter if you provided it.
Even if you had that parameter, though, you would not be allowed to write something like def process(self, formatting = self.formatting), because self is not in scope yet at the point where you set the default value. In Python, the default value for a parameter is calculated when the function is compiled, and "stuck" to the function. (This is the same reason why, if you use a default like [], that list will remember changes between calls to the function.)
How could I make this work?
The traditional way is to use None as a default, and check for that value and replace it inside the function. You may find it is a little safer to make a special value for the purpose (an object instance is all you need, as long as you hide it so that the calling code does not use the same instance) instead of None. Either way, you should check for this value with is, not ==.
Since you want to use self.format as a default argument this implies that the method needs to be instance specific (i.e. there is no way to define this at class level). Instead you can define the specific method during the class' __init__ for example. This is where you have access to instance specific attributes.
One approach is to use functools.partial in order to obtain an updated (specific) version of the method:
from functools import partial
class C:
def __init__(self, format):
self.format = format
self.process = partial(self.process, formatting=self.format)
def process(self, formatting):
print(formatting)
c = C('default')
c.process()
# c.process('custom') # Doesn't work!
c.process(formatting='custom')
Note that with this approach you can only pass the corresponding argument by keyword, since if you provided it by position, this would create a conflict in partial.
Another approach is to define and set the method in __init__:
from types import MethodType
class C:
def __init__(self, format):
self.format = format
def process(self, formatting=self.format):
print(formatting)
self.process = MethodType(process, self)
c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')
This allows also passing the argument by position, however the method resolution order becomes less apparent (which can affect the IDE inspection for example, but I suppose there are IDE specific workarounds for that).
Another approach would be to create a custom type for these kind of "instance attribute defaults" together with a special decorator that performs the corresponding getattr argument filling:
import inspect
class Attribute:
def __init__(self, name):
self.name = name
def decorator(method):
signature = inspect.signature(method)
def wrapper(self, *args, **kwargs):
bound = signature.bind(*((self,) + args), **kwargs)
bound.apply_defaults()
bound.arguments.update({k: getattr(self, v.name) for k, v in bound.arguments.items()
if isinstance(v, Attribute)})
return method(*bound.args, **bound.kwargs)
return wrapper
class C:
def __init__(self, format):
self.format = format
#decorator
def process(self, formatting=Attribute('format')):
print(formatting)
c = C('test')
c.process()
c.process('custom')
c.process(formatting='custom')
You can't access self in the method definition. My workaround is this -
class Test:
def __init__(self):
self.default_v = 20
def test(self, v=None):
v = v or self.default_v
print(v)
Test().test()
> 20
Test().test(10)
> 10
"self" need to be pass as the first argument to any class functions if you want them to behave as non-static methods.
it refers to the object itself. You could not pass "self" as default argument as it's position is fix as first argument.
In your case instead of "formatting=self.format" use "formatting=None" and then assign value from code as below:
[EDIT]
class c:
def __init__(self, cformat):
self.cformat = cformat
def process(self, formatting=None):
print "Formating---",formatting
if formatting == None:
formatting = self.cformat
print formatting
return formatting
else:
print formatting
return formatting
c("abc").process() # prints "abc"
c("abc").process("xyz") # prints "xyz"
Note : do not use "format" as variable name, 'cause it is built-in function in python
Instead of creating a list of if-thens that span your default arguements, one can make use of a 'defaults' dictionary and create new instances of a class by using eval():
class foo():
def __init__(self,arg):
self.arg = arg
class bar():
def __init__(self,*args,**kwargs):
#default values are given in a dictionary
defaults = {'foo1':'foo()','foo2':'foo()'}
for key in defaults.keys():
#if key is passed through kwargs, use that value of that key
if key in kwargs: setattr(self,key,kwargs[key])
#if no key is not passed through kwargs
#create a new instance of the default value
else: setattr(self,key, eval(defaults[key]))
I throw this at the beginning of every class that instantiates another class as a default argument. It avoids python evaluating the default at compile... I would love a cleaner pythonic approach, but lo'.

How To Call A Class Function From A Variable

I need a class router (for lack of a better word). The router needs to instantiate a class & call a function of that class instance based on variables passed to it.
How do I properly define the class function?
How do I properly call the class function?
Example code:
class ClassWorker1:
def function_1(self):
print('1a')
def function_2(self):
print('2a')
def function_3(self):
print('3a')
class ClassWorker2:
def function_1(self):
print('1b')
def function_2(self):
print('2b')
def function_3(self):
print('3b')
class ClassRouter(object):
def __init__(self, class_name, class_function):
self.class_instance = class_name()
self.class_function = class_function
self.main()
def main(self):
# how should I call the class function here?
self.class_instance.class_function()
return
a = 1
b = 1
if a == 1:
class_name = ClassWorker1
else:
class_name = ClassWorker1
if b == 1:
# Strings won't work as class function definition
# I won't know the class at this point. I will only know
# the shared function name at this point.
# how could this class function be defined directly?
class_function = 'function_1'
elif b == 2:
class_function = 'function_2'
else:
class_function = 'function_3'
ClassRouter(class_name, class_function)
I need a class router (for lack of a better word).
Are you sure you need a class for this ?
The router needs to instantiate a class & call a function of that class instance
When it belongs to a class or instance, a function is usually named a "method". Not really important but it makes things clearer. Also, an "instance" is obviously always, by definition, an instance of a class ;)
How do I properly define the class function?
How do I properly call the class function?
Does the router really have to be a class ? But anyway...
There are a couple distinct issues here (I of course assume you need something that's generic enough).
The first one is that your class (the one that will be instanciated by the "router") constructor may need some args - position or named or both. If it's the router's duty to instanciate the class (but should it be ?), you'll have to pass those args (both position and named) to the router. And since your router has to be generic (else it's useless) you cannot explicitely name these args in your router's constructor.
Hopefully, Python has a way to "unpack" tuples (for position args) and dicts (for named args) when calling a function, using respectively the * and ** operators at call time, ie:
def somefunc(arg1, arg2, arg3="foo", arg4=None):
print arg1, arg2, arg3, arg4
args = ("one", "two", "three")
kwargs = {"arg4": "four"}
somefunc(*args, **kwargs)
This let you pass arguments to a function in a generic way.
So if you want your router to be in charge of instanciating the "target" class, you'll have to support this:
class Router(object):
def __init__(self, cls, clsargs=None, clskwargs=None):
if clsargs is None:
clsargs = ()
if clskwargs is None:
clskwargs = {}
self._obj = cls(*clsargs, **clskwargs)
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker, clsargs=("foo",))
# or
r = Router(Worker, clskwargs={"name":"foo"})
Now note that at this point you really don't gain anything (except for more code) from having the router instanciating the Worker - since you need to have the Worker class and it's constructor's args to instanciate the router, you could as well just instanciate the Worker yourself and pass the Worker instance to the router:
Since you must have a reference to the class passed to the router (else you can't pass it ), you could as well
class Router(object):
def __init__(self, obj):
self._obj = obj
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker("foo"))
# or
r = Router(Worker(name="foo"))
The cases where it would make sense to have the router instanciate the worker are:
1/ if the Worker's constructor arguments are not known when the router is instanciated and are to be passed later (which requires a distinct router method to pass those args)
2/ if the Worker's instanciation is very costly and you're not even sure you'll really need it, in which case you want to wait until the router's "main" method is called to instanciate the worker.
The second issue is "how do I get the worker's method by name". This one has already been answered by Lukas: you use getattr(obj, attrname).
The third issue is "if my worker method needs arguments, how do I pass them". This is the same problem as with the worker's constructor arguments, so the solution is obviously the same. Depending on the concrete use case, you'll have to pass those args either when instanciating the router or when calling it's "main" method.
wrt/ this "main" method, remember that you can define your own callable types by implementing the __call__ method, ie
class NotAFunc(object):
def __init__(self, wot):
self.wot = wot
def __call__(self, count):
print self.wot * count
notafunc = NotAFunc("wot ? ")
notafunc(42)
So it might make sense to use this as your router's "main" method
Now do you really need a router class at all ? Python functions are object on their own (so a function can take a function and/or return a function), and moreover act as closures (a closure is a function that "captures" part of the environment where it's been defined):
def route(instance, methodname, methargs=None, methkwargs=None):
method = getattr(instance, methodname)
if methargs is None:
methargs = ()
if methkwargs is None:
methkwargs = {}
def func():
return method(*methargs, **methkwargs)
return func
class Worker(object):
def __init__(self, name):
self.name = name
def work(self, count):
return [self.name for i in range(count)]
r = route(Worker("foo"), "work", (42,))
print r()
Note that while I kept your "router" term, most of what I described above are known patterns. You may want to search for "proxy", "proxy method", and (for the last exemple) "partial evaluation".
You are looking for dynamic attribute lookup.
class C:
def c1(self, x):
return 2*x
instance = C()
method_name = 'c1'
method = getattr(instance, method_name)
print(method(1)) # call method and print result
You'll need to override the __new__ method of your (new-style!) class.
class ClassRouter(object):
def __new__(self, class_name, *args):
if arg=="Class1":
new_instance = ClassWorker1(*args)
new_instance.method()
return new_instance
elif arg=="Class2":
return ClassWorker2(*args)

Python - If a function is a first class object, can a function have a method?

I have a class which maintains a list of functions. These functions are just objects sitting in a queue and every so often the class pops one off and executes it. However, there are times when I would like to print out this list, and I'm imagining code as follows:
for function in self.control_queue:
print function.summarize()
if function.ready():
function()
In other words, I would like to call methods called summarize() and ready(), that I want to define somewhere, on these function objects. Also, I would like to be able to toss anonymous functions on this queue - i.e., generate everything dynamically.
you can make it a class and define __call__
class MyClass():
def summarize(self):
#summarize stuff
pass
def ready(self):
#ready stuff
pass
def _call__(self):
#put the code here, for when you call myClass()
pass
How you run it:
function = MyClass()
print function.summarize()
if function.ready():
function()
You have a couple possible approaches.
You could add the definitions to functions.
def foo():
pass
# later..
foo.summarize = lambda: "To pair with bar"
foo.ready = lambda: True
You could create class objects to wrap the function operation.
class Func():
def summarize(self):
return "Function!"
def ready(self):
return self.ready
def __call__(self):
# Act as a function
Or you can have a function which checks the function label for these capabilities.
def summarize_func(func):
return func.__name__ # Or branch here on specific names/attributes
def ready_func(func):
return True # Or branch on names/attributes
Finally to accommodate anonymous functions you can check for prescience of these attributes and return optimistically if the attributes are absent. Then you can combine above approaches with something that will work on any function.
def summarize_func(func):
if hasattr(func, summarize):
return func.summarize()
else:
# Note this will just be '<lambda>' for anonymous funcs
return func.__name__
def ready_func(func):
if hasattr(func, ready):
return func.ready()
else:
return True
One option is to implement function as a class instance:
class Function(object):
def summarize(self): pass # some relevant code here
def __call__(self): pass # and there
and use it later with
function = Function()
With __call__ magic method implemented, this function becomes a callable object.
For sure, you can assign attributes to functions, but it is rather obscure and conterintuitive:
>>> def summ(a): return sum(a)
...
>>> def function(a): return a
...
>>> function.sum=summ
>>> function.sum([1,2,3])
6

Python metaprogramming: automatically generate member functions

How do I write a function that adds a method to a class? I have:
class A:
def method(self):
def add_member(name):
self.new_method = def name...?
add_member("f1")
add_member("f2")
In order to answer what I'm trying to do. I'm trying to factor out some pyqt slots. I want to be able to call a function create_slider that will create a QSlider and a QLabel and create the slider handling code, and make the slider-handler update the text in the QLabel. Here's the slot that needs to be factored out:
def on_sample_slider(self, value):
self.samples = pow(4, value)
self.sample_label.setText('%d' % self.samples)
here's a method that generates some UI, but it would be nice to also have it generate the on_sample_slider method every time it is called:
def insert_labeled_slider(hbox, name, slider_target):
# name
hbox.addWidget(QLabel(name))
# label
label = QLabel()
label.setMinimumSize(40, 0)
hbox.addWidget(self.sample_label)
#slider
slider = QSlider(Qt.Horizontal)
slider.setRange(0, 6)
slider.setTracking(True)
slider.setPageStep(1)
hbox.addWidget(slider)
self.connect(self.sample_slider, SIGNAL('valueChanged(int)'),
self.on_sample_slider)
self.sample_slider.setValue(0)
return (label, slider)
Final code:
def attach_on_slider(obj, name, variable, label, base):
def on_slider(self, value):
variable = base**value
label.setText('%d' % variable)
# This next line creates a method from the function
# The first arg is the function and the second arg is the object
# upon which you want it to be a method.
method = types.MethodType(on_slider, obj)
obj.__dict__["on_slider_" + name] = method
return method
class A:
def insert_labeled_slider(hbox, name, label_name, variable):
# name
hbox.addWidget(QLabel(label_name))
# label
label = QLabel()
label.setMinimumSize(40, 0)
hbox.addWidget(label)
#slider
slider = QSlider(Qt.Horizontal)
slider.setRange(0, 6)
slider.setTracking(True)
slider.setPageStep(1)
hbox.addWidget(slider)
on_slider_method = attach_on_slider(self, name, variable, label, 4)
self.connect(slider, SIGNAL('valueChanged(int)'),
on_slider_method)
slider.setValue(0)
return (label, slider)
Here's an real example from your newly posted code:
import types
def attach_on_sample_slider(obj, base):
def on_sample_slider(self, value):
self.samples = base**value
self.sample_label.setText('%d' % self.samples)
# This next line creates a method from the function
# The first arg is the function and the second arg is the object
# upon which you want it to be a method.
obj.on_sample_slider = types.MethodType(on_sample_slider, obj)
You can now call it like
def some_method(self, foo):
attach_on_sample_slider(self, 4)
original post
Since you say the the member functions are identical, I would do it something like this
def make_method(name):
def method(self, whatever, args, go, here):
#whatever code goes here
method.__name__ = name
return method
class A(object):
method1 = make_method('method1')
method2 = make_method('method2')
Strictly speaking, passing in the name and setting the __name__ attribute on the new function isn't necessary but it can help with debugging. It's a little bit of duplication and can pay for itself. If you are going to skip that though, you might as well do
class A(object):
def method1(self, arg1, arg2):
#code goes here
method2 = method1
method3 = method1
This creates identical methods. Calling either of them will yield the same method.
The first form is more powerful because you can pass other arguments besides the name into make_method and have the different versions of the returned method access those parameters in closure so they work differently. Here's a stupid example with functions (works the same with methods):
def make_opener(filename):
def opener():
return open(filename)
return opener
open_config = make_opener('config.cfg')
open_log = make_opener('log.log')
Here, they're all essentially the same function but do slightly different things because they have access to the value of filename that they were created with. Closures are definitely something to look into if you're going to be doing a lot of this sort of thing.
There can be a lot more to this so if you have particular questions that this doesn't address, you should update your question.
Add bound methods to instantiated objects in Python tells this issue.

Categories