I suppose I'm missing something obvious, but I can't get the name of methods when I'm using decorators. When I run this code, I get the error:
AttributeError: 'str' object has no attribute "__name__"
Could somebody tell me how I can get the name of these decorated method?
Thanks
def Print(*arg, **kwarg):
func, *arguments = arg
print(func.__name__ + "(): {}".format(func=arguments[0]))
class Bob(object):
def __init__(self):
pass
#property
def stuff(self):
return "value from stuff property"
#stuff.setter
def stuff(self, noise):
return noise
class Tester:
def __init__(self):
self.dylan = Bob()
def randomTest(self):
Print(self.dylan.stuff, 1)
if __name__ == "__main__":
whatever = Tester()
whatever.randomTest()
stuff isn't a function or a method; it's a property. The syntax
#property
def stuff(...):
...
creates an instance of the property class using stuff as the argument to property, equivalent to
def stuff(...):
....
stuff = property(stuff)
and instances of property don't have a __name__ attribute, as you've seen.
(It's a little trickier with the setter, since the function and the property have to have the same name. But defining stuff a "second" time doesn't override the existing property named stuff.)
The individual methods are accessed via attributes of the property.
>>> Bob.stuff.fget.__name__
'stuff'
>>> Bob.stuff.fset.__name__
'stuff'
Note another, longer, way to create the same property:
class Bob:
def stuff_getter(self):
...
def stuff_setter(self, noise):
...
stuff = property(stuff_getter, stuff_setter)
del stuff_getter, stuff_setter # Clean up the namespace
def Print(*arg, **kwarg):
func, *arguments = arg
print(func.__name__ + "(): {}".format(func=arguments[0]))
class Bob():
def __init__(self, s):
self.stuff = s
#property
def myStuff(self):
return self.stuff
#myStuff.setter
def setStuff(self, noise):
self.stuff = noise
class Tester:
def __init__(self):
self.dylan = Bob(1)
def randomTest(self):
print(self.dylan.stuff)
if __name__ == "__main__":
whatever = Tester()
whatever.randomTest()
This should work :)
Related
In python, how can I setup a parent class to track methods with a specific decorator for each child seperatly? A quick code snippet of what I am trying to do:
class Parent:
decorated_func_dict = {} #dictionary that stores name->func for decorated functions
def get_func_by_decorator_name(self, name):
#stuff
pass
class Child1(Parent):
#func_name("Bob")
def bob_func(self, *args):
pass
#func_name("Tom")
def func2(self, *args):
pass
class Child2(Parent):
#func_name("Bob")
def func_bob2(self, *args):
pass
foo = Child1()
bar = Child2()
foo.get_func_by_decorator_name("Bob")
#Returns foo.bob_func
bar.get_func_by_decorator_name("Bob")
#Returns bar.func_bob2
Using Python 3.9.
A decorator is not something that makes a function look pretty. It is a callable that ingests an object (not only functions), does some arbitrary operations, and returns a replacement object.
In this case, your decorator should be storing references to function objects in a dictionary somewhere. The problem is that you won't be able to reference the class in which the functions are defined until it is created, which happens well after the decorator is run. You can avoid this by storing the name of the class as well as the name of the function.
The final step here is to properly bind the function objects to methods on the right object. That is something that get_func_by_decorated_name can do for you.
In sum, you can write something like this:
decorated_func_dict = {}
def func_name(cls_name, func_name):
def decorator(func):
decorated_func_dict.setdefault(cls_name, {})[func_name] = func
return func
return decorator
class Parent:
def get_func_by_decorator_name(self, name):
return decorated_func_dict[type(self).__name__][name].__get__(self)
class Child1(Parent):
#func_name("Child1", "Bob")
def bob_func(self, *args):
pass
#func_name("Child1", "Tom")
def func2(self, *args):
pass
class Child2(Parent):
#func_name("Child2", "Bob")
def func_bob2(self, *args):
pass
And indeed you get:
>>> foo.get_func_by_decorator_name("Bob")
<bound method Child1.bob_func of <__main__.Child1 object at 0x000001D58181E070>>
>>> bar.get_func_by_decorator_name("Bob")
<bound method Child2.func_bob2 of <__main__.Child2 object at 0x000001D582041F10>>
Another way to do this is to give your functions a name attribute, which you can then aggregate into a mapping in __init_subclass__ in Parent. This allows you to make an interface a bit closer to what you originally intended:
def func_name(func_name):
def decorator(func):
func.special_name = func_name
return func
return decorator
class Parent:
def __init_subclass__(cls):
cls.decorated_func_dict = {}
for item in cls.__dict__.values():
if hasattr(item, 'special_name'):
cls.decorated_func_dict[item.special_name] = item
del item.special_name # optional
def get_func_by_decorator_name(self, name):
return self.decorated_func_dict[name].__get__(self)
class Child1(Parent):
#func_name("Bob")
def bob_func(self, *args):
pass
#func_name("Tom")
def func2(self, *args):
pass
class Child2(Parent):
#func_name("Bob")
def func_bob2(self, *args):
pass
The results are identical to the first example.
The easiest way would of course be to get access to the child's namespace before the class is created, e.g. with a metaclass.
I'm trying to store specific actions that are defined within a class.
To reduce code duplication, I would like to make use of a mixin class that stores all the actions based on a decorator.
The idea is that it should be straightforward for other people to extend the classes with new actions. I especially want to avoid that these actions are explicitly listed in the source code (this should be handled by the decorator).
This is what I came up with. Unfortunately, in all .actions lists, all the actions from all the classes are listed.
However, I would like to have a solution that only the actions of the specific class are listed.
class ActionMixin:
actions = []
#staticmethod
def action(fun):
ActionMixin.actions.append(fun)
return fun
class Human(ActionMixin):
#ActionMixin.action
def talk(self):
pass
class Dog(ActionMixin):
#ActionMixin.action
def wuff(self):
pass
class Cat(ActionMixin):
#ActionMixin.action
def miau(self):
pass
if __name__ == "__main__":
party = [Human(), Dog()]
possible_actions = [action for memer in party for action in member.actions]
# I would like that possible_actions is now only Human.talk() and Dog.wuff()
# instead it is 2 times all actions
print(len(possible_actions)) # == 6
I would just write my own descriptor here. So:
class Registry:
def __init__(self):
self._registered = []
def __call__(self, func):
self._registered.append(func)
return func
def __get__(self, obj, objtype=None):
return self._registered
class Human:
actions = Registry()
#actions
def talk(self):
pass
class Dog:
actions = Registry()
#actions
def wuff(self):
pass
class Cat:
actions = Registry()
#actions
def miau(self):
pass
So, instead of inheriting from a mixin, just initialize the descriptor object. Then that object itself can be used as the decorator (the __call__ method!).
Note, the decorator would be whatever name you assigned it, and it would be the name of the attribute where the actions are stored.
In the REPL:
In [11]: party = [Human(), Dog()]
In [12]: [action for member in party for action in member.actions]
Out[12]: [<function __main__.Human.talk(self)>, <function __main__.Dog.wuff(self)>]
EDIT:
You would have to change the implementation if you want this to live in a base class. Basically, use a dict to keep track of the registries, unfortunately, we have to rely on the brittle __qualname__ to get the class in __call__:
class ActionsRegistry:
def __init__(self):
self._registry = {}
def __call__(self, func):
klass_name, func_name = func.__qualname__.rsplit('.', 1)
if klass_name not in self._registry:
self._registry[klass_name] = []
self._registry[klass_name].append(func)
return func
def __get__(self, obj, objtype=None):
if obj is None:
return self
return self._registry[objtype.__qualname__]
class Base:
actions = ActionsRegistry()
class Human(Base):
#Base.actions
def talk(self):
pass
class Dog(Base):
#Base.actions
def wuff(self):
pass
class Cat(Base):
#Base.actions
def miau(self):
pass
I want to do something like:
class A(Resource):
#dec(from_file=A.docpath)
def get(self):
pass
class B(A):
docpath = './docs/doc_for_get_b.json'
class C(A):
docpath = './docs/doc_for_get_c.json'
def dec(*args, **kwargs):
def inner(f):
docpath = kwargs.get('from_file')
f.__kwargs__ = open(path, 'r').read()
return f
return inner
The functions that will be called are B.get and C.get, never A.get.
How can I access the custom attribute docpath defined in class B or class C and pass it to the decorator of the get function in class A ?
Current solution: Put the decorator on each derived class ...
class A(Resource):
def _get(self):
pass
class B(A):
#dec(from_file='./docs/doc_for_get_b.json')
def get(self):
return self._get()
class C(A)
#dec(from_file='./docs/doc_for_get_c.json')
def get(self):
return self._get()
This works but it's pretty ugly compared to the one-line declaration of the classes in the previous code.
To access a class's attributes inside the decorator is easy:
def decorator(function):
def inner(self):
self_type = type(self)
# self_type is now the class of the instance of the method that this
# decorator is wrapping
print('The class attribute docpath is %r' % self_type.docpath)
# need to pass self through because at the point function is
# decorated it has not been bound to an instance, and so it is just a
# normal function which takes self as the first argument.
function(self)
return inner
class A:
docpath = "A's docpath"
#decorator
def a_method(self):
print('a_method')
class B(A):
docpath = "B's docpath"
a = A()
a.a_method()
b = B()
b.a_method()
In general I've found using multiple levels of decorators, i.e. decorator factory functions that create decorators such as you've used and such as:
def decorator_factory(**kwargs):
def decorator_function(function):
def wrapper(self):
print('Wrapping function %s with kwargs %s' % (function.__name__, kwargs))
function(self)
return wrapper
return decorator_function
class A:
#decorator_factory(a=2, b=3)
def do_something(self):
print('do_something')
a = A()
a.do_something()
a difficult thing to get right and not easy to comprehend when reading code, so I would err towards using class attributes and generic superclass methods in favour of lots of decorators.
So in your case, don't pass the file path in as an argument to your decorator factory, but set it as a class attribute on your derived classes, and then write a generic method in your superclass that reads the class attribute from the instance's class.
I have a big class which has a lot of functions and attributes.
the instances are created from data in a remote database.
the process of creating each instance is very long and heavy.
In performance sake ive created a bunch class from this heavy class.
so accessing the attributed is easy and works great .
the problem is how to use the methods from that class.
ex :
class clsA():
def __init__(self,obj):
self.attrA=obj.attrA
def someFunc(self):
print self
class bunchClsA(bunch):
def __getattr__(self, attr):
# this is the problem:
try:
#try and return a func
func = clsA.attr
return func
except:
# return simple attribute
return self.attr
Clearly this dosent work , Is there a way i could access the instance function staticly and override the "self" var ?
Found out a nice solution to the problem :
from bunch import Bunch
import types
#Original class:
class A():
y=6
def __init__(self,num):
self.x=num
def funcA(self):
print self.x
#class that wraps A using Bunch(thats what i needed .. u can use another):
class B(Bunch):
def __init__(self, data, cls):
self._cls = cls # notice, not an instance just the class it self
super(B, self).__init__(data)
def __getattr__(self, attr):
# Handles normal Bunch, dict attributes
if attr in self.keys():
return self[attr]
else:
res = getattr(self._cls, attr)
if isinstance(res, types.MethodType):
# returns the class func with self overriden
return types.MethodType(res.im_func, self, type(self))
else:
# returns class attributes like y
return res
data = {'x': 3}
ins_b = B(data, A)
print ins_b.funcA() # returns 3
print ins_b.y # returns 6
And this solves my issue, its a hack and if you have the privileges, redesign the code.
I'm tying to create a class that holds a reference to another classes method. I want to be able to call the method. It is basically a way to do callbacks.
My code works until I try to access a class var. When I run the code below, I get the error What am I doing wrong?
Brian
import logging
class yRunMethod(object):
"""
container that allows method to be called when method run is called
"""
def __init__(self, method, *args):
"""
init
"""
self.logger = logging.getLogger('yRunMethod')
self.logger.debug('method <%s> and args <%s>'%(method, args))
self.method = method
self.args = args
def run(self):
"""
runs the method
"""
self.logger.debug('running with <%s> and <%s>'%(self.method,self.args))
#if have args sent to function
if self.args:
self.method.im_func(self.method, *self.args)
else:
self.method.im_func(self.method)
if __name__ == "__main__":
import sys
#create test class
class testClass(object):
"""
test class
"""
def __init__(self):
"""
init
"""
self.var = 'some var'
def doSomthing(self):
"""
"""
print 'do somthing called'
print 'self.var <%s>'%self.var
#test yRunMethod
met1 = testClass().doSomthing
run1 = yRunMethod(met1)
run1.run()
I think you're making this WAY too hard on yourself (which is easy to do ;-). Methods of classes and instances are first-class objects in Python. You can pass them around and call them like anything else. Digging into a method's instance variables is something that should almost never be done. A simple example to accomplish your goal is:
class Wrapper (object):
def __init__(self, meth, *args):
self.meth = meth
self.args = args
def runit(self):
self.meth(*self.args)
class Test (object):
def __init__(self, var):
self.var = var
def sayHello(self):
print "Hello! My name is: %s" % self.var
t = Test('FooBar')
w = Wrapper( t.sayHello )
w.runit()
Why not use this:
self.method(*self.args)
instead of this:
if self.args:
self.method.im_func(self.method, *self.args)
else:
self.method.im_func(self.method)
In your code you were calling self.method.im_func(self.method) - you shouldn't have been passing the method as argument but the object from which that method came. I.e. should have been self.method.im_func(self.method.im_self, *self.args)