Forwarding methods to a wrapped class - python-3.x

I'd like to attach some extra functionality (logging more or less) to methods of another class, but with the caveat that I can't subclass them, since I do not have control over the instantiation of the original class, but I can forward an object with the same duck typing. In other words, I'd like to prepend some code to a few methods while forwarding everything else to the original class instance.
An example of what I'm trying to accomplish would be something like the following:
class A(object):
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, name):
return getattr(self._wrapped, name)
class B(object):
def foo(self):
print("foo")
#classmethod
def bar():
print("bar")
b = B()
a = A(b)
a.foo() # prints "foo"
a.bar() # Fails (tries to pass the self parameter to bar())
Except that the method above fails for class methods, so it won't do it in my case. Is there a way to forward calls like this in a way that also works with class methods and static methods?

Related

Python: why do I need super().__init__() call in metaclasses?

I have got one question: why do I need to call super().--init--() in metaclasses? Because metaclass is factory of classes, I think we don`t need to call initialization for making objects of class Shop. Or with using super().--init-- we initializing the class? (Because my IDE says, that I should call it. But without super().--init-- nothing happens, my class working without mistakes).
Can you explane me, why?
Thanks in advance!
class Descriptor:
_counter = 0
def __init__(self):
self.attr_name = f'Descriptor attr#{Descriptor._counter}'
Descriptor._counter += 1
def __get__(self, instance, owner):
return self if instance is None else instance.__dict__[self.attr_name]
def __set__(self, instance, value):
if value > 0:
instance.__dict__[self.attr_name] = value
else:
msg = 'Value must be > 0!'
raise AttributeError(msg)
class Shop():
weight = Descriptor()
price = Descriptor()
def __init__(self, name, price, weight):
self.name = name
self.price = price
self.weight = weight
def __repr__(self):
return f'{self.name}: price - {self.price} weight - {self.weight}'
def buy(self):
return self.price * self.weight
class Meta(type):
def __init__(cls, name, bases, attr_dict):
super().__init__(name, bases, attr_dict) # <- this is that func. call
for key, value in attr_dict.items():
if isinstance(value, Descriptor): # Here I rename attributes name of descriptor`s object.
value.attr_name = key
#classmethod
def __prepare__(metacls, name, bases):
return OrderedDict()
You don't "need" to - and if your code use no other custom metaclasses, not calling the metaclass'__init__.super() will work just the same.
But if one needs to combine your metaclass with another, through inheritance, without the super() call, it won't work "out of the box": the super() call is the way to ensure all methods in the inheritance chain are called.
And if at first it looks like that a metaclass is extremely rare, and combining metaclasses would likely never take place: a few libraries or frameworks have their own metaclasses, including Python's "abc"s (abstract base classes), PyQT, ORM frameworks, and so on. If any metaclass under your control is well behaved with proper super() calls on the __new__, __init__ and __call__ methods, (if you override those), what you need to do to combine both superclasses and have a working metaclass can be done in a single line:
CompatibleMeta = type("CompatibleMeta", (meta, type(OtherClassBase)), {})
This way, for example, if you want to use the mechanisms in your metaclass in a class using the ABCMeta functionalities in Python, you just do it. The __init__ method in your Meta will call the other metaclass __init__. Otherwise it would not run, and some subtle unexpectd thing would not be initialized in your classes, and this could be a very hard to find bug.
On a side note: there is no need to declare __prepare__ in a metaclass if all it does is creating an OrderedDict on a Python newer than 3.6: Since that version, dicitionaries used as the "locals()" while executing class bodies are ordered by default. Also, if another metaclass you are combining with also have a __prepare__, there is no way to make that work automatically by using "super()" - you have to check the code and verify which of the two __prepare__s should be used, or create a new mapping type with features to attend both metaclasses.

Python class method also instance method

I have a class that in principle carries all the information about it in its class body. When instantiated, it receives additional information that together with the class attributes forms a regular instance. My problem now lies in the fact that I need to implement a method which should be called as class method when it is called from a class object but should be called as regular instance method when called from an instance:
e.g. something like
class MyClass(object):
attribs = 1, 2, 3
def myMethod(self, args):
if isclass(self):
"do class stuff"
else:
"do instance stuff"
MyClass.myMethod(2) #should now be called as a class method, e.g. I would normally do #classmethod
MyClass().myMethod(2) #should now be called as instance method
Of course I could declare it as staticmethod and pass either the instance or the class object explicitly, but that seems rather unpythonic and also user unfriendly.
If the methods are to behave differently, you could simply change which one is exposed by that name at initialization time:
class MyCrazyClass:
#classmethod
def magicmeth(cls):
print("I'm a class")
def _magicmeth(self):
print("I'm an instance")
def __init__(self):
self.magicmeth = self._magicmeth
You can define a decorator that works like a regular method when called on an instance, or class method when called on a class. This requires a descriptor:
from functools import partial
class anymethod:
"""Transform a method into both a regular and class method"""
def __init__(self, call):
self.__wrapped__ = call
def __get__(self, instance, owner):
if instance is None: # called on class
return partial(self.__wrapped__, owner)
else: # called on instance
return partial(self.__wrapped__, instance)
class Foo:
#anymethod
def bar(first):
print(first)
Foo.bar() # <class '__main__.Foo'>
Foo().bar() # <__main__.Foo object at 0x106f86610>
Note that this behaviour will not be obvious to most programmers. Only use it if you really need it.

Find owner class of a method in Python

I'm writing decorators, and part of what I need to do is discern whether a function is a function or a method. Is there a way I can find what class a given method is a part of?
e.g. If I was to run this code, what could I write in getOwner to make exampleFunc print something like <class '__main__'.Example>?
class Example:
def method(self):
print("I'm a method")
def exampleFunc(func):
owner = getOwner(func)
print(owner)
test = Example()
exampleFunc(test.method)
If all you need to do is figure out of the thing behaving like a function is a method or a function, that is one purpose of the types module.
import types
def is_method(f):
return type(f) == types.MethodType
In the event that the function-like object is a method, you can find its parent class as follows.
Update Patched for Python3 compatibility.
def method_parent(f):
return f.__self__
If you have a reference to the classes defined in your scope, you'd need to check for each one:
def exampleFunc(f):
class_list = [...]
return any(f in vars(c).values() for c in class_List)
This will return True if function f is an instance method. However, if you wish to return the actual class name:
def exampleFunc(f):
class_list = [...]
for c in class_list:
if f in vars(c).values():
return c.__name__
return 'global function' if 'lambda' not in f.__name__ else 'lambda'
Note that this does not work for __dunder__ methods, and methods that your class inherits. For example,
class A:
def f1(self): pass
class B(A):
def f2(self): pass
print(vars(B))
mappingproxy({'__doc__': None,
'__module__': '__main__',
'f2': <function __main__.B.f2>})
Note that f1 is not a part of B's mappingproxy.

How do I bind to a function inside a class and still use the event variable?

class Player():
def __init__():
...
def moveHandle(self, event):
self.anything = ...
box.bind("<Key>", Player.moveHandle)
The bind function sets self as the event variable and ignores/throws up an error for event. I can't find a way to pass the event argument to the correct variable and maintain self for that function, even if I use args*. I can do one or the other, but not both.
I'm probably just lacking some basic knowledge about classes to be honest, I taught them to myself and didn't do it very thoroughly.
If I made a syntax mistake, it's because of me rewriting out the code incorrectly; in my program, the code works until the variables get passed.
the problem is that you are trying to use an instance method as a class method.
consider the following:
class Player():
def __init__():
...
def moveHandle(self, event):
self.anything = ...
box.bind("<Key>", Player.moveHandle)
where box is an instance of something, but Player is not.
instead this:
class Player():
def __init__(self):
...
def moveHandle(self, event):
self.anything = ...
p = Player()
box.bind("<Key>", p.moveHandle)
creates an instance of the player class, and then binds to the instances method, not the class method.

Can I derive from classmethod in Python?

I have a special statemachine implemented in Python, which uses class methods as state representation.
class EntityBlock(Block):
def __init__(self, name):
self._name = name
#classmethod
def stateKeyword1(cls, parserState : ParserState):
pass
#classmethod
def stateWhitespace1(cls, parserState : ParserState):
token = parserState.Token
if isinstance(token, StringToken):
if (token <= "generate"):
parserState.NewToken = GenerateKeyword(token)
parserState.NewBlock = cls(....)
else:
raise TokenParserException("....", token)
raise TokenParserException("....", token)
#classmethod
def stateDelimiter(cls, parserState : ParserState):
pass
Visit GitHub for full source code off pyVHDLParser.
When I debug my parser FSM, I get the statenames printed as:
State: <bound method Package.stateParse of <class 'pyVHDLParser.DocumentModel.Sequential.Package.Package'>>
I would like to get better reports, so I would like to overwrite the default behavior of __repr__ of each bound method object.
Yes, I could write a metaclass or apply a second decorator, but I was questioning myself:
Is it possible to derive from classmethod and have only one decorator called e.g. state?
According to PyCharm's builtins.py (a collection of dummy code for Python's builtins), classmethod is a class-based decorator.
Yes, you can write your own class that derives from classmethod if you want. It's a bit complicated though. You'll need to implement the descriptor protocol (overriding classmethod's implementation of __get__) so that it returns an instance of another custom class that behaves like a bound method object. Unfortunately, you can't inherit from Python's builtin bound method type (I'm not sure why not).
Probably the best approach then is to wrap one of the normal method objects in an instance of a custom class. I'm not sure how much of the method API you need to replicate though, so that might get a bit complicated. (Do you need your states to be comparable to one another? Do they need to be hashable? Picklable?)
Anyway, here's a bare bones implementation that does the minimum amount necessary to get a working method (plus the new repr):
class MethodWrapper:
def __init__(self, name, method):
self.name = name if name is not None else repr(method)
self.method = method
def __call__(self, *args, **kwargs):
return self.method(*args, **kwargs)
def __repr__(self):
return self.name
class State(classmethod):
def __init__(self, func):
self.name = None
super().__init__(func)
def __set_name__(self, owner, name):
self.name = "{}.{}".format(owner.__name__, name)
def __get__(self, owner, instance):
method = super().__get__(owner, instance)
return MethodWrapper(self.name, method)
And a quick demo of it in action:
>>> class Foo:
#State
def foo(cls):
print(cls)
>>> Foo.foo
Foo.foo
>>> Foo.foo()
<class '__main__.Foo'>
>>> f = Foo()
>>> f.foo()
<class '__main__.Foo'>
Note that the __set_name__ method used by the State descriptor is only called by Python 3.6. Without that new feature, it would be much more difficult for the descriptor to learn its own name (you might need to make a decorator factory that takes the name as an argument).

Resources