How to dynamically add method to class with `functools.partial()` - python

I am having trouble with the right incantation to get a dynamic method added to a class using functools.partial in the following situation. The following has a Creator class to which I want to add a create_someclass method, which is partially parameterized by the creator class state.
import functools
class Creator:
def __init__(self, params):
self.params = params
class Stitch:
__tablename__ = 'stitch'
def __init__(self, params, name):
self.name = name
self.params = params
def create(self, clz, *args, **kwargs):
return clz(self.params, *args, **kwargs)
for clazz in [Stitch]:
setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partial(create, clz=clazz))
creator = Creator('params')
# Neither of these work, but I'd like either one -- preferably the first one.
stitch = creator.create_stitch('myname')
# AttributeError: 'str' object has no attribute 'params'
stitch = creator.create_stitch(name='myname')
# TypeError: create() missing 1 required positional argument: 'self'

This is a problem for making partial for class methods, so in Python 3.4 we introduced partialmethod as the alternative. The way that works is the following:
import functools
class Creator:
def __init__(self, params):
self.params = params
class Stitch:
__tablename__ = 'stitch'
def __init__(self, params, name):
self.name = name
self.params = params
def create(self, clz, *args, **kwargs):
return clz(self.params, *args, **kwargs)
for clazz in [Stitch]:
setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partialmethod(create, clz=clazz))
# use partialmethod instead here
creator = Creator('params')
stitch = creator.create_stitch(name='myname')
# works!

I think the problem is that create is a member function of Stitch (despite your bad indentation: create accesses the member variable params of Stitch), so you would need an object of type Stitch to use with create, which would then also be passed as the first argument to create. It would work like this:
import functools
class Creator:
def __init__(self, params):
self.params = params
class Stitch:
__tablename__ = 'stitch'
def __init__(self, params, name):
self.name = name
self.params = params
def create(self, clz, *args, **kwargs):
return clz(self.params, *args, **kwargs)
creator = Creator('params')
stitch1 = Stitch('pp', 'my_name')
print("stitch1= ", stitch1)
for clazz in [Stitch]:
setattr(Creator, 'create_%s' % clazz.__tablename__, functools.partial(stitch1.create, clazz))
stitch = creator.create_stitch('myname')
print("stitch= ", stitch)

Related

Call function from child class in python

This is my python code.
class Model:
def __init__(self, name=None):
if name is None:
name = get_apnx_service_name(self.__class__.__name__)
print("Object created for", name)
self.url_suffix = "/{name}/".format(name=name)
self.name = name
self.apnx_session = sessiona()
def get_all(self, start_element=0, num_elements=100, method='GET', fields=None, params=None, **kwargs):
if params is None:
params = {}
if is_list(fields):
params['fields'] = join_list(fields)
params["start_element"] = start_element
params["num_elements"] = num_elements
res = self.apnx_session.request(method, url_suffix=self.url_suffix, params=params, **kwargs)
return res
class Split(Model):
def __init__(self, name="/budget-splitter/{li}/splits"):
super().__init__(name=name)
self.format_str = name
def get_all(self, li_id, method='GET', **kwargs):
self.url_suffix = self.format_str.format(li=li_id)
#super().get_all(**kwargs)**kwargs)
#here
I needed to change url_suffix in get_all() from Split class.I changed in Split class get_all method.I want to call the output of get_all() in child class.How can i do this.I don't want to do like this solution of writing same code again in Split class.
class Split(Model):
def __init__(self, name="/budget-splitter/{li}/splits"):
super().__init__(name=name)
self.format_str = name
def get_all(self, start_element=0, num_elements=100, method='GET', fields=None, params=None, **kwargs):
if params is None:
params = {}
if is_list(fields):
params['fields'] = join_list(fields)
params["start_element"] = start_element
params["num_elements"] = num_elements
res = self.apnx_session.request(method, url_suffix=self.url_suffix, params=params, **kwargs)
return res
SOLVED
using return super(Split, self).get_all(**kwargs)
What if an extra argument, li_id, were added to the constructor for class Split and url_suffix was initialized during construction (or should I say re-initialized after the base class has already initialized it)? It would then seem that there would be no need to override method get_all. Could this work?
class Split(Model):
def __init__(self, li_id, name="/budget-splitter/{li}/splits"):
super().__init__(name=name)
self.url_suffix = name.format(li=li_id)

Can I change the default behavior of my parent class method in my subclass method?

I'm learning simple python inheritance and I want that one of my parent class method default argument is changed conditionally to one of my subclass argument value, and I don't know if this is possible.
Here is an example of what I'd like to do:
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict = True):
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super(Child, self).__init__(name)
if 'changeBehavior' in kwargs:
# Here is the thing:
# Can I change the default value of strict to kwargs['changeBehavior']
# in a way that when I later call doSomething(), it will behave according
# to its new default behavior?
def doSomething(self, name, strict = kwargs['changeBehavior']):
super(Child, self).doSomething(strict = kwargs['changeBehavior'])
If this can be done in this way, is there any other method to do so?
Thanks
You can use partial.
from functools import partial
class Parent(object):
def __init__(self, name):
self.name = name
def doSomething(self, name, strict=True):
print('Got strict={}'.format(strict))
if strict:
return self.name
else:
return name
class Child(Parent):
def __init__(self, name, **kwargs):
super().__init__(name)
change_behavior = kwargs.get('changeBehavior')
if change_behavior is not None:
self.doSomething = partial(self.doSomething, strict=change_behavior)
p = Parent('name')
c = Child('name', changeBehavior=False)
p.doSomething('name')
c.doSomething('name')
outputs
Got strict=True
Got strict=False

How to use a class method to decorate other class methods?

When designing a class, I find that in the class methods, there are repeated steps are called each time when invoke the class method. For example:
class Queue(object):
def __init__(self):
self.connection = Connection()
def create(self, name):
self.probe = self.connection.plug()
self.probe.create(name)
self.probe.unplug()
def delete(self, name):
self.probe = self.connection.plug()
self.probe.delete(name)
self.probe.unplug()
And there are many methods require the similar steps to 'plug' and 'unplug' the 'probe'. In this design we need to 'plug' and 'unplug' the 'probe' each time we perform the actions.
Thus I am thinking about the wrap those functions by decorator to make the code looking less repeated.
class Queue(object):
def __init__(self):
self.connection = Connection()
def _with_plug(self, fn):
def wrapper(*args, **kwargs):
self.probe = self.connection.plug()
fn(*args, **kwargs)
self.probe.unplug()
#_with_plug
def create(self, name):
self.probe.create(name)
#_with_plug
def delete(self, name):
self.probe.delete(name)
But this strategy is not working. How could I use a method in the class to decorate other methods to perform such actions before and after calling a method?
Seems like a bit of muddled arguments to me:
file deco.py, say
def _with_plug(fn): # decorator takes exactly one argument, the function to wrap
print("wrapping", fn.__name__)
def wrapper(self, *args, **kwds):
print("wrapper called")
self.probe = [self.connection, ".plug()"]
fn(self, *args, **kwds)
self.probe.append(".unplug()")
return wrapper # decorator must return the wrapped function
class Queue(object):
def __init__(self):
self.connection = "Connection()"
#_with_plug
def create(self, name):
self.probe.append("create(name)")
#_with_plug
def delete(self, name):
self.probe.append("delete(name)")
Check:
>>> import deco
wrapping create
wrapping delete
>>> q = deco.Queue()
>>> q.create("name")
wrapper called
>>> q.probe
['Connection()', '.plug()', 'create(name)', '.unplug()']
Observe that the decorator function is called at definition time of the to-be-wrapped function, i.e. before the class definition is completed and long before the first instance is created. Therefore you can't reference self in the way you tried.
You should define your decorator function outside of the class body and your decorator function should return the wrapped function in order for it to work. Something like:
def _with_plug(fn):
def wrapper(self, *args, **kwargs):
self.probe = self.connection.plug()
fn(self, *args, **kwargs)
self.probe.unplug()
return wrapper
class Queue(object):
def __init__(self):
self.connection = Connection()
#_with_plug
def create(self, name):
self.probe.create(name)
#_with_plug
def delete(self, name):
self.probe.delete(name)

Issues with nesting inheritance - how to initialize parent class?

I am confused on why the code below does not work:
class ComparativeAnnotatorConfiguration(HashableNamespace):
"""
Takes the initial configuration from the main driver script and builds paths to all files that will be produced
by these tasks.
"""
def __init__(self, args, gene_set, query_genome_files, target_genome_files, annot_files, transmap):
self.work_dir = os.path.join(args.workDir, 'comparativeAnnotator', gene_set.sourceGenome, gene_set.geneSet)
self.metrics_dir = os.path.join(args.outputDir, 'metrics')
self.tx_set_dir = os.path.join(args.outputDir, 'tm_transcript_set')
self.reference = self.Reference(args, query_genome_files, annot_files, self.work_dir)
self.transmap = self.TransMap(args, query_genome_files, target_genome_files, annot_files, transmap, self.work_dir)
class Reference(HashableNamespace):
"""
The args object that will be passed directly to jobTree
"""
def __init__(self, args, query_genome_files, annot_files, out_dir):
self.__dict__.update(vars(args.jobTreeOptions))
self.outDir = out_dir
self.refGenome = query_genome_files.genome
self.refFasta = query_genome_files.genome_fasta
self.sizes = query_genome_files.chrom_sizes
self.annotationGp = annot_files.gp
self.gencodeAttributes = annot_files.attributes
self.mode = 'reference'
class TransMap(Reference):
"""
The args object that will be passed directly to jobTree
"""
def __init__(self, args, query_genome_files, target_genome_files, annot_files, transmap, out_dir):
super(self.__class__, self).Reference.__init__(self, args, query_genome_files, annot_files, out_dir)
self.genome = target_genome_files.genome
self.psl = transmap.psl
self.refPsl = annot_files.psl
self.targetGp = transmap.gp
self.fasta = target_genome_files.fasta
self.mode = 'transMap'
Attempting to instantiate leads to the error:
AttributeError: 'super' object has no attribute 'Reference'
I have tried different versions such as super(TransMap, self).Reference.__init__ and Reference.__init__, but all give different versions of a NameError. How is this different than the simple case outlined here:
Using super() in nested classes
You want this:
super(ComparativeAnnotatorConfiguration.TransMap, self).__init__(...)
This is a consequence of Python's class scoping rules: class variables are not in scope inside methods. This does not change just because your "variable" is itself a class. As far as Python is concerned, that's exactly the same thing.
In Python 3, you can write the far simpler:
super().__init__(...)
This is yet another reason to upgrade.
You could use super(ChildClass, self).__init__()
class BaseClass(object):
def __init__(self, *args, **kwargs):
pass
class ChildClass(BaseClass):
def __init__(self, *args, **kwargs):
super(ChildClass, self).__init__(*args, **kwargs)
Sample Inheritance and initializing parent constructor code :
class Car(object):
condition = "new"
def __init__(self, model, color, mpg):
self.model = model
self.color = color
self.mpg = mpg
class ElectricCar(Car):
def __init__(self, battery_type, model, color, mpg):
self.battery_type=battery_type
super(ElectricCar, self).__init__(model, color, mpg)
car = ElectricCar('battery', 'ford', 'golden', 10)
print car.__dict__
Here's the output:
{'color': 'golden', 'mpg': 10, 'model': 'ford', 'battery_type': 'battery'}
super(self.__class__, self).__init__()
will call the __init__ method of parent class.

How to pass self into a decorator?

How do I pass self.key below into the decorator?
class CacheMix(object):
def __init__(self, *args, **kwargs):
super(CacheMix, self).__init__(*args, **kwargs)
key_func = Constructor(
memoize_for_request=True,
params={'updated_at': self.key}
)
#cache_response(key_func=key_func)
def list(self, *args, **kwargs):
pass
class ListView(CacheMix, generics.ListCreateAPIView):
key = 'test_key'
I get the error:
'self' is not defined
Here's an example of doing it with a class decorator as I tried to describe to you in the comments. I filled-in a few undefined references in your question and used a super-simplified version of your cache_response function decorator, but hopefully this will convey the idea concretely enough for you to be able adapt it to your real code.
import inspect
import types
class Constructor(object):
def __init__(self, memoize_for_request=True, params=None):
self.memoize_for_request = memoize_for_request
self.params = params
def __call__(self):
def key_func():
print('key_func called with params:')
for k, v in self.params.items():
print(' {}: {!r}'.format(k, v))
key_func()
def cache_response(key_func):
def decorator(fn):
def decorated(*args, **kwargs):
key_func()
fn(*args, **kwargs)
return decorated
return decorator
def example_class_decorator(cls):
key_func = Constructor( # define key_func here using cls.key
memoize_for_request=True,
params={'updated_at': cls.key} # use decorated class's attribute
)
# create and apply cache_response decorator to marked methods
# (in Python 3 use types.FunctionType instead of types.UnboundMethodType)
decorator = cache_response(key_func)
for name, fn in inspect.getmembers(cls):
if isinstance(fn, types.UnboundMethodType) and hasattr(fn, 'marked'):
setattr(cls, name, decorator(fn))
return cls
def decorate_me(fn):
setattr(fn, 'marked', 1)
return fn
class CacheMix(object):
def __init__(self, *args, **kwargs):
super(CacheMix, self).__init__(*args, **kwargs)
#decorate_me
def list(self, *args, **kwargs):
classname = self.__class__.__name__
print('list() method of {} object called'.format(classname))
#example_class_decorator
class ListView(CacheMix):
key = 'test_key'
listview = ListView()
listview.list()
Output:
key_func called with params:
updated_at: 'test_key'
list() method of ListView object called
I just found out that if you write the decorator function like so:
def decorator(the_func):
#wraps(the_func)
def wrapper(*args, **kwargs):
the_func(*args, **kwargs)
return wrapper
and decorate any method which takes self as an argument, self will appear in args. Therefore you can do this:
from functools import wraps
class myClass:
def __init__(self):
self.myValue = "Hello"
def decorator(the_func):
#wraps(the_func)
def wrapper(*args, **kwargs):
print(args[0].myValue)
the_func(*args, **kwargs)
return wrapper
#decorator
def myFunction(self):
print("World")
Call it like you normally would
foo = myClass()
foo.myFunction()
and you should get
Hello
World

Categories