Call function from child class in python - 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)

Related

How to decorate an instance with code-blocks and accessing/using the variables of the instance?

I try to build a decorator for methods of instances (not classes) that flexibly puts code blocks in front and/or behind the method (and not affect other instances). Up to my code below works:
def Decorate(func, before = None, after = None):
def wrap(*args, **kwargs):
if before: before() # code block insert
result = func(*args, **kwargs)
if after: after() # code block insert
return result
return wrap
class Test():
def __init__(self, name):
self.name = name
def put(self, prefix):
print(prefix, self.name)
a = Test('me')
def Before():
print('before')
def After():
print('after')
a.put = Decorate(a.put, Before, After)
a.put('it is')
How can I extend the code blocks accessing/using variables and methods of the instance? A code example for this would look like this:
def Before():
print('before')
print(self.name)
self.any_method(any_argument) # just an example!
I already tried several things without success. And I already struggle to access the instance values directly in the wrapper:
def Decorate(func, before = None, after = None):
def wrap(self, *args, **kwargs):
if before: before() # code block insert
print(self.name) # --> even this DOES NOT WORK!
result = func(self, *args, **kwargs)
if after: after() # code block insert
return result
return wrap
Here print(self.name) throws an error: AttributeError: 'str' object has no attribute 'name'. So it looks like that I am far away in using the same comment in one of the code blocks (Before() & After()) below.
One addition: The approach works when I add a method to the instance:
This method is in the class (so for working with strings and exec, but that enables to deliver the name as string or the function itself):
def addMethod(self, method, givenName = ''):
print('add')
if givenName == '':
N = method.__name__
else:
N = givenName
self._methods.append(N)
exec('self.' + N + ' = ' + method.__name__ + '.__get__(self)')
The code in the main part looks like this:
def x(self):
print('hello,', self.name)
a.addMethod(x)
a.x()
Any solution is appreciated and many thanks in advance!
from functools import wraps
def Decorate(func, before = None, after = None):
#wraps(func)
def wrap(*args, **kwargs):
if before: before() # code block insert
result = func(*args, **kwargs)
if after: after() # code block insert
return result
return wrap
def Before():
print('before')
def After():
print('after')
class Test():
def __init__(self, name):
self.name = name
def put(self, prefix):
print(prefix, self.name)
put = Decorate(put, Before, After)
a = Test('me')
a.put("pre")
You could execute your decorator inside your class. In your wrap, pass whatever you get to func by (*args, **kwargs). self is still the first argument implicitly in args.
Edit: Code related concerns from comments
from functools import wraps
def Before(t):
print('before')
print(t.name)
def After(t):
print('after')
print(t.name)
def Decorate(func, before = None, after = None):
#wraps(func)
def wrap(*args, **kwargs):
if before: before(args[0]) # code block insert
result = func(*args, **kwargs)
if after: after(args[0]) # code block insert
return result
return wrap
class Test():
def __init__(self, name):
self.name = name
def put(self, prefix):
print(prefix, self.name)
put = Decorate(put, Before, After)
a = Test('me')
a.put("pre")

Creating unique instance works, but how to get the reference when calling the class again?

I am trying to create a class that stores all created instances in the class itself by using new and init magical methods together and I want to return an existing class if it was created before.
My current code looks like this:
class Obj():
_list = []
def __new__(cls, id, *args, **kwargs):
print('new')
for o in Obj._list:
if o.id == id:
print('existent')
return # 'return o' DOES NOT WORK!
instance = super(Obj, cls).__new__(cls, *args, **kwargs)
return instance
def __init__(self, id, *args, **kwargs):
print('init')
super().__init__(*args, **kwargs)
self.id = id
Obj._list.append(self)
Obj(1)
Obj(1)
The code works as it does not produce doubles in the Obj._list (second call of Obj(1) prints 'existent' and cancels the creation of another instance). But when I change the line return in the new-function to return o then two instances are created though I want a pointer / the name of the existent instance to be returned and not the creation of another instance.
Any chance to achieve this behaviour with this code or any other solution? I am trying to avoid a function inside the class like:
def get(id):
for i in Obj._list:
if i.id == id:
return i
Obj.get(1).my_method_call()
Any help is appreciated! Many thanks in advance!
Ulrich
Here's a modified version of an answer I gave to a similar question:
def singleton_id(cls):
instances={}
def getinstance(id, *args, **kwargs):
key = "{}__{}".format(cls, id)
if key not in instances:
instances[key] = cls(id, *args, **kwargs)
return instances[key]
return getinstance
#singleton_id
class Obj():
def __init__(self, id, *args, **kwargs):
super().__init__(*args, **kwargs)
self.id = id
print(id(Obj(1)) == id(Obj(1))) # True
print(id(Obj(1)) == id(Obj(2))) # False
As you can see by the output of the print statements, the objects created with the same ID are identical.
Since you want to access the already created instances, I've modified the answer even more.
Note: I've removed the cls from the key, so this will no longer work as a decorator for different classes with the same key, but this seems to be no requirement in your case.
def singleton_id(cls):
def getinstance(id, *args, **kwargs):
if id not in singleton_id.instances:
singleton_id.instances[id] = cls(id, *args, **kwargs)
return singleton_id.instances[id]
return getinstance
singleton_id.instances = {}
#singleton_id
class Obj():
def __init__(self, id, *args, **kwargs):
super().__init__(*args, **kwargs)
self.id = id
print(id(Obj(1)) == id(Obj(1))) # True
print(id(Obj(1)) == id(Obj(2))) # False
print(singleton_id.instances[2].id) # 2
Finally I brushed up the solution from Mike into a fully working code example (including adding methods to instances and deleting instances). So you have a fully working object handler in Python (e.g. good for games). The only downside of this you can see in the last line of code: if you have deleted an instance and you try to call it then a new, empty one is set up immediately. Here's the full solution:
def singleton_id(cls):
singleton_id.instances = {}
def getinstance(id, *args, **kwargs):
if id not in singleton_id.instances:
singleton_id.instances[id] = cls(id, *args, **kwargs)
return singleton_id.instances[id]
return getinstance
#singleton_id
class Obj():
def __init__(self, id, *args, **kwargs):
print('init')
super().__init__(*args, **kwargs)
self._methods = []
self.id = id
def delete(self):
del singleton_id.instances[self.id]
def addMethod(self, method, givenName = ''):
print('add')
if givenName == '':
N = method.__name__
else:
N = givenName
self._methods.append(N)
exec('self.' + N + ' = ' + method.__name__ + '.__get__(self)')
def executeMethod(self, method):
if type(method) != str:
method = method.__name__
if method in self._methods:
exec('self.' + method + '()')
def put(self):
print('I am', self.id)
# testing
Obj(1) # creation
Obj(1) # call
Obj(1).put()
print(Obj(1))
def x(self):
print('hello,', self.id)
Obj(1).addMethod(x)
# can be called in different ways:
Obj(1).x()
Obj(1).executeMethod(x)
Obj(1).executeMethod('x')
Obj(1).delete()
Obj(1).put() # immediately creates new instance!

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

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)

Python "requests" with decorator

There is my code I'm trying to do:
import json
import requests
class Request(object):
def __init__(self, method, path):
self.method = method
## Q1
self.url = ''.join([***MyClass.host***, path])
self.h = {
"Content-Type": "application/json;charset=UTF-8"
}
def __call__(self, f):
def wrapper(*args, **kwargs):
# Q2
getattr(requests, self.method)(url = self.url, headers = h***, data = json.dumps(body)***)
return wrapper
class MyClass(object):
def __init__(self, host):
self.host = host
#Request(method = "post", path = "/add_mission")
def AddMission(self, mission):
body = {
"mission_id": mission
}
#Request(method = "get", path = "/system_info")
def GetInfo(self):
print("I want get info")
There are some questions hope someone can solve my problems:
1. How can my decorator "Request" get the variable "host" from "MyClass"? (In comment ## Q1)
2. How can I pass the function's variable "body" to decorator, is it possible? (In comment ## Q2)
Because I need to access different url(host+path) with [post, get, delete].
I'm not sure isn't decorator match on this case?
Or maybe there are better ways to deal with this case?
The host is available once you call the decorated function, via the invoking object's host attribute, so wait until you actually call the method to build the URL. You'll want to call the underlying function to get back a value, if any, to use as the payload.
import json
import requests
class Request(object):
def __init__(self, method, path):
self.method = method.upper()
self.h = {
"Content-Type": "application/json;charset=UTF-8"
}
self.path = path
def __call__(self, f):
def wrapper(obj, *args, **kwargs):
payload = f(*args, **kwargs)
args = {
'method': self.method,
'url': 'http://{}/{}'.format(obj.host, self.path),
'headers': self.h
}
if payload is not None:
args['json'] = payload
return requests.request(**args)
return wrapper
class MyClass(object):
def __init__(self, host):
self.host = host
#Request(method="post", path="/add_mission")
def AddMission(self, mission):
return {"mission_id": mission}
#Request(method="get", path="/system_info")
def GetInfo(self):
print("I want get info")
Then you can write, for example,
api = MyClass("www.example.com")
add_response = api.AddMission("mars")
info_response = api.GetInfo()
Each decorated function returns the Response object produced by the call to requests.request.
You might consider subclasses of Request for each method:
class GetRequest(Request):
def __init__(self, path):
super().__init__("GET", path)
class PostRequest(Request):
def __init__(self, path):
super().__init__("POST", path)
# etc
The code now has all types of requests covered, plus the possibility of having authentication on each request.
Code on github
Pros:
Easy to read, less code, therefore easier to maintain.
Easy to add new methods or to update the existing ones.
Cons:
Can't get it to work on pytest.
Less "standard" API might cause hard to debug errors.
Less "standard" API might need changes to the wrapper that can be tricky.
Big performance loss because of all the nested function calls (methods/decorators are functions)
import ujson
import requests
class Request(object):
def __init__(self, method, path, h={ "Content-Type": "application/json;charset=UTF-8" }):
self.path = path
#print(f"PATH {path}")
self.method = method.upper()
self.h = h
def __call__(self, f):
def wrapper(obj, *args, **kwargs):
payload = f(obj, *args, **kwargs)
#print(f"PAYLOAD {payload}")
if (payload is not None) and (self.method == "GET" or self.method == "DELETE"):
dic_key = list(payload.keys())[0]
self.path = "{}/?{}={}".format(self.path, dic_key, payload[dic_key])
payload = None
args = {
'method': self.method,
'url': 'http://{}/{}'.format(obj.host, self.path),
'headers': self.h
}
if payload is not None:
args['json'] = payload
return requests.request(**args)
return wrapper
class GetRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("GET", path)
else:
super().__init__("GET", path, h)
class PostRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("POST", path)
else:
super().__init__("POST", path, h)
class PutRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("PUT", path)
else:
super().__init__("PUT", path, h)
class DeleteRequest(Request):
def __init__(self, path, h=None):
if h is None:
super().__init__("DELETE", path)
else:
super().__init__("DELETE", path, h)
myToken = 'Secret_key_1234567890'
class DecoRequests(object):
def __init__(self, host):
self.host = host
#GetRequest(path="userlist", h={'access_token':myToken})
def list_employees(self):
pass
#GetRequest(path="users", h={'access_token':myToken})
def list_employee(self, username):
return {"username":username}
#PostRequest(path="users", h={'access_token':myToken})
def add_employee(self, employee_data):
return employee_data
#PutRequest(path="users", h={'access_token':myToken})
def update_employee(self, employee_data):
return employee_data
#DeleteRequest(path="users", h={'access_token':myToken})
def rm_employee(self, username):
return {"username":username}

Python adding an argument/attribute do derived class' constructor

I have a class Record :
class Record(object):
def __init__(self, id=None, url=None, time= None, user=None, input_dict=None):
if input_dict:
self.from_dict(input_dict)
else:
self.id = id
self.url = url
self.time = time
self.user = user
def from_dict_internal(self, input_dict):
#creates a record using data from a dictionary
self.id = input_dict.get('id')
self.url = input_dict.get('url')
self.time = input_dict.get('time')
self.user = input_dict.get('user')
def from_dict(self, input_dict):
return self.from_dict_internal()
I want to make a derived class Page with 1 extra attribute - referrer. I am not sure how to go about it as I have tried this:
class Page(Record):
def __init__(self):
Record.__init__(self)
self.referrer = referrer
def from_dict(self, input_dict):
self.from_dict_internal()
self.referrer = input_dict.get('referrer')
However Pycharm keeps underlining the referrer in the constructor of page with a reason of unresolved reference
You are getting an error because referrer is not declared anywhere or passed into the init method. Also you should pass in the same data that your Record() class uses.
def __init__(self, id=None, url=None, time= None, user=None, input_dict=None, referrer=None):
Record.__init__(self, id, url, time, user, input_dict)
self.referrer = referrer
....

Categories