I have what is essentially the following in python:
class X(object): pass
class Y(object):
@classmethod
def method(cls, x_inst, *args, **kwargs):
#do some work here that requires an instance of x
pass
What I would like to do is add a dynamic property to all instances of X
allowing acces to Y
that implicitly fills in the first parameter of the method
with the given instance. E.G I would like the following code to work identically:
# current
x = X()
result = Y.method(x, 1, 2, 3)
# desired
x = X()
x.Y.method(1, 2, 3)
There are several methods on several subclasses that I would like to implement this behaviour for. What I have done currently is to create a YProxy
class that X
actually returns, and then put split some of the code into that. It seems rather inelegant and hard to maintain however:
class X(object):
@property
def Y(self):
return YProxy(self)
class Y(object):
@classmethod
def method(cls, x_inst, *args, **kwargs):
#do some work here that requires an instance of x
pass
class YProxy(object):
def __init__(self, x_inst):
self.x_inst = x_inst
def method(self, *args, **kwargs):
return Y.method(self.x_inst, *args, **kwargs)
Is there any way to conditionally partially evaluate the classmethods on an object?
What is Class Method in Python. Class methods are methods that are called on the class itself, not on a specific object instance. Therefore, it belongs to a class level, and all class instances share a class method. A class method is bound to the class and not the object of the class. It can access only class variables ...
cls accepts the class Person as a parameter rather than Person's object/instance. Now, we pass the method Person. printAge as an argument to the function classmethod . This converts the method to a class method so that it accepts the first parameter as a class (i.e. Person).
Class methods don't need a class instance. They can't access the instance ( self ) but they have access to the class itself via cls .
To call a class method, put the class as the first argument. Class methods can be can be called from instances and from the class itself. All of these use the same method. The method can use the classes variables and methods.
It can be done with a Descriptor object + a wrapper class ad explicitly declaring the classes you need to wrap on this way on your target class.
A descriptor object is any object defining a __get__
method, which allows one to customize the attribute retrieving when the descriptor is part of a class. On this case, we want that when that attribute - which is the "Y" class - is retrieved from an instance, whenever a method is retrieved from that class, the instance is inserted on the parameter list.
This requires that the attribute retrieved be itself a "proxy" class with custom attribute access to allow the dynamic wrapping to take note.
Translating all this into Python, we have:
import types
from functools import partial
class APIWrapper(object):
def __init__(self, apicls, instance):
self._apicls = apicls
self._instance = instance
def __getattribute__(self, attr):
apicls = object.__getattribute__(self, "_apicls")
instance = object.__getattribute__(self,"_instance")
obj = getattr(apicls, attr)
if isinstance(obj, types.MethodType):
return partial(obj,instance)
return obj
class APIProperty(object):
def __init__(self, cls):
self.cls = cls
def __get__(self, instance, cls):
return APIWrapper(self.cls, instance)
class Y(object):
@classmethod
def method(cls, x, *args):
print cls, x, args
class X(object):
Y = APIProperty(Y)
#Example usage:
x = X()
x.Y.method(1,2,3)
(prints <class '__main__.Y'> <__main__.X object at 0x18ad090> (1, 2, 3)
when run)
But I suppose you don't want to need to write
Y = APIWrapper(Y)
for each of the classes you want to wrap on this way. (And have those classes defined after the wrapped class so that Y already has been parsed when X body is parsed).
This can be done with metaclasses, class decorators, which would have to be defined for each class you'd want to apply the methods - instead, I made a function that is to be called at the end of the module definition, where you define your "X" class - this function will add the desired classes as attributes for each class defined (in my example, I want the class to be marked with an "auto_api" attribute - but suit yourself) - Thus, the "auto_api" function, the definition of the X and Y classes becomes like this (using the same APIProperty and APIWrapper as above)
def auto_api(api_classes, glob_dict):
for key, value in glob_dict.items():
if isinstance(value, type) and hasattr(value, "auto_api"):
for api_class in api_classes:
setattr(value, api_class.__name__, APIProperty(api_class))
class X(object):
auto_api = True
class Y(object):
@classmethod
def method(cls, x, *args):
print cls, x, args
auto_api((Y,), globals())
#Example
x = X()
x.Y.method(1,2,3)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With