I'm trying to write a python class which uses a decorator function that needs information of the instance state. This is working as intended, but if I explicitly make the decorator a staticmetod, I get the following error:
Traceback (most recent call last):
File "tford.py", line 1, in <module>
class TFord(object):
File "tford.py", line 14, in TFord
#ensure_black
TypeError: 'staticmethod' object is not callable
Why?
Here is the code:
class TFord(object):
def __init__(self, color):
self.color = color
#staticmethod
def ensure_black(func):
def _aux(self, *args, **kwargs):
if self.color == 'black':
return func(*args, **kwargs)
else:
return None
return _aux
#ensure_black
def get():
return 'Here is your shiny new T-Ford'
if __name__ == '__main__':
ford_red = TFord('red')
ford_black = TFord('black')
print ford_red.get()
print ford_black.get()
And if I just remove the line #staticmethod, everything works, but I do not understand why. Shouldn't it need self as a first argument?
This is not how staticmethod is supposed to be used. staticmethod objects are descriptors that return the wrapped object, so they only work when accessed as classname.staticmethodname. Example
class A(object):
#staticmethod
def f():
pass
print A.f
print A.__dict__["f"]
prints
<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>
Inside the scope of A, you would always get the latter object, which is not callable.
I'd strongly recommend to move the decorator to the module scope -- it does not seem to belong inside the class. If you want to keep it inside the class, don't make it a staticmethod, but rather simply del it at the end of the class body -- it's not meant to be used from outside the class in this case.
Python classes are created at runtime, after evaluating the contents of the class declaration. The class is evaluated by assigned all declared variables and functions to a special dictionary and using that dictionary to call type.__new__ (see customizing class creation).
So,
class A(B):
c = 1
is equivalent to:
A = type.__new__("A", (B,), {"c": 1})
When you annotate a method with #staticmethod, there is some special magic that happens AFTER the class is created with type.__new__. Inside class declaration scope, the #staticmethod function is just an instance of a staticmethod object, which you can't call. The decorator probably should just be declared above the class definition in the same module OR in a separate "decorate" module (depends on how many decorators you have). In general decorators should be declared outside of a class. One notable exception is the property class (see properties). In your case having the decorator inside a class declaration might make sense if you had something like a color class:
class Color(object):
def ___init__(self, color):
self.color = color
def ensure_same_color(f):
...
black = Color("black")
class TFord(object):
def __init__(self, color):
self.color = color
#black.ensure_same_color
def get():
return 'Here is your shiny new T-Ford'
Solution does exist!
Problem is that Static method that is trying to be used as decorator is in fact staticmethod object and is not callable.
Solution: staticmethod object has method __get__ which takes any argument and returns real method: python documentation Python 3.5 and up:
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
Min solution I came with is:
class A():
def __init__(self):
self.n = 2
#staticmethod
def _returnBaseAndResult(func):
from functools import wraps
#wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
response = func(*args, **kwargs)
return self.n, response
return wrapper
#_returnBaseAndResult.__get__('this can be anything')
def square(self):
return self.n**2
if __name__ == '__main__':
a = A()
print(a.square())
Will print (2, 4)
ensure_black is returning a _aux method that isn't decorated by #staticmethod
You can return a non-static method to a static_method
http://docs.python.org/library/functions.html#staticmethod
Related
I want to save the class name and class itself into a python dict by using a decorator.
from functools import wraps
models = {} # python dict which save class name and class
def register_model(name):
def register(func):
#wraps(func)
def inner(name):
models[name] = func
return inner(name)
return register
# `A` is a class that I want to save into the dict.
#register_model('1244')
class A(object):
a = 1
def __init__(self):
super(A, self).__init__()
# But when call it below:
print(models['1244']().a)
I get an error:
Traceback (most recent call last):
File "/Data/Usr/t.py", line 50, in <module>
print(models['1244']().a)
File "/Data/Usr/t.py", line 36, in __init__
super(A, self).__init__()
TypeError: super() argument 1 must be type, not None
I solve this error by changing super(A, self).__init__() to super().__init__()
I want to know why augment 1 is None and what cause it.
The decorator doesn't return anything, and whatever it returns will be assigned to the name A (the class definition). You're making the class accessible solely through models['1244'], A() as such doesn't work (because A is None). This is what's happening when you do super(A, ...).
You don't need to pass A to super, just super().__init__() will do just fine. This would resolve this particular problem.
Your decorator should return the class.
In fact, your decorator is overly complicated. inner is completely superfluous. Change it to this to solve both issues:
def register_model(name):
def register(cls):
models[name] = cls
return cls
return register
Now it just registers the class in models and otherwise lets it pass through unchanged.
Your decorator returns None, so A in the original scope ends up being None, and super(A, self) with A = None is an invalid call. The simple fix is to return func from the decorator.
Simplifying your code and getting rid of outdated idioms such as inheriting from object and super(..., ...), things work fine:
models = {}
def register_model(name):
def register(cls):
models[name] = cls
return cls
return register
#register_model('1244')
class A:
a = 1
def __init__(self):
super().__init__()
print("Hi!")
print(models['1244']().a)
I am trying to mimic a descriptor with a decorator function, but have failed. Here's what I have tried.
def my_property(self):
def wrapper(func):
return func(self)
return wrapper
class C:
def __init__(self):
self._x = 0
#my_property(C()) # this will print 0 for me, but it's not from the obj `c`
def p(self):
return self._x
c = C()
print(c.p)
Overriding __get__ directly in the decorator didn't help me either. I also tried to inherit the function class and override its __get__ method, but was told function is final and not subclassable (probably I should try forbiddenfruit https://github.com/clarete/forbiddenfruit).
Can anyone help?
Edit: To clarify, I was wondering if it is possible without having to use the keyword class?
Your intuition is right to override __get__. In fact, that's exactly how properties in Python actually work.
Consider
class MyProperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.func(instance)
MyProperty is a class whose instances contain a func. When we __get__ an instance of MyProperty off a class, it'll call the inner function automatically.
Now we'll make a neat wrapper function for it. Pretty trivial, but still nice to be consistent with Python's nomenclature for functions vs. types
def my_property(func):
return MyProperty(func)
Finally, using the decorator.
class C:
def __init__(self):
self._x = 0
#my_property
def p(self):
return self._x
c = C()
print(c.p) # Prints 0
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 class ABC, inside it there is a function someFunction.
I do not want to mess up with the code of someFunction, thus I wrap it with a #MyDecorator. How can this MyDecorator modify the property of the class ABC when I call someFunction?
class ABC(object):
def __init__(self):
self.someProperty = "Initial value"
#MyDecorator
def someFunction(self):
print "Hello world"
class MyDecorator(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
# ...???
return self.func(*args, **kwargs)
abc = ABC()
abc.someFunction() # When calling someFunction(), abc.someProperty would be updated
You can try:
class MyDecorator(object):
def __init__(self, func):
self.func = func
def __call__(self, instance, *args, **kwargs):
instance.someProperty = "New value"
return self.func(instance, *args, **kwargs)
def __get__(self, instance, owner):
if instance is None:
return self
return self.__call__.__class__(self, instance)
class ABC(object):
def __init__(self):
self.someProperty = "Initial value"
#MyDecorator
def someFunction(self):
print("Hello world")
MyDecorator is now a descriptor since it implements __get__.
When an attribute of any instance of MyDecorator is accessed through some class or an instance of that class, __get__ will be called.
__get__ will receive an instance of that owner object in the parameter instance, from there we can return a new instance of instancemethod that will receives that instance implicitly:
>>> abc.someFunction.__class__
<type 'instancemethod'>
>>> abc.someFunction.__class__.__doc__
'instancemethod(function, instance, class)\n\nCreate an instance method object.'
>>>
It requires a function, basically any callable object will work, because MyDecorator is class, __call__ will revise its own self.
In action:
>>> abc = ABC()
>>> abc.someProperty
'Initial value'
>>> abc.someFunction()
Hello world
>>> abc.someProperty
'New value'
>>>
Note in Python 2.X, MyDecorator must be a new-style class that inherits object or that won't work.
You need to define MyDecorator earlier in the file than where you use it, or you get "NameError: name 'MyDecorator' is not defined".
I was wrong when I said "a decorator should return a function". That applies to functions used as decorators, as in my example. However, when a class is used, it doesn't have to return a function as such. It returns an object in that class. It might be less confusing if "class MyDecorator" were named "class MyModifiedFunction" instead, because an element of the class is used as the decorated function.
Usually, the function returned by the decorator will call the original function. Yours doesn't. It doesn't have to if you want to totally replace someFunction rather than just modify it.
You don't need to make MyDecorator a class. You could make it a function. Here's an example:
def MyDecorator(original_function):
def replacement_function(*args, **kwargs):
args[0].some_property = some_new_value
return(original_function(*args, **kwargs))
return(replacement_function)
Follow this by the first half of your code, where you define class ABC.
Note that when it says return(original_function(*args, **kwargs)) it's running the original function and returning whatever the original function returns. If the original function returned an object, it would return that object. However, when it says return(replacement_function) it's doing something quite different: it's not running the replacement function at that time; it's just returning the function as the return value. A decorator has to return a function.
(#dano: Good point. I fixed it, I think.)
Here's the above suggestion in more complete form:
def MyDecorator(original_function):
def replacement_function(*args, **kwargs):
args[0].some_property = 'Modified value'
return(original_function(*args, **kwargs))
return(replacement_function)
class ABC(object):
def __init__(self):
self.some_property = "Initial value"
#MyDecorator
def some_function(self):
print "Hello world"
abc = ABC()
abc.some_function() # When calling some_function(), abc.some_property wou
ld be updated
print "some property is", abc.some_property
I ran this and it works; it prints
Hello world
some property is Modified value
so this verifies that it did indeed modify the some_property in abc. It's accessed as args[0].some_property, because when the decorated function is called (as an object method), self is passed in as the first argument.
This question already has answers here:
Attaching a decorator to all functions within a class
(11 answers)
Closed 5 years ago.
I have several classes and they have same implements name but difference realization. I want to decorate all methods in some classes but others not. I have thought about inheritance, but some classes have some methods do not need to be decorated. The problem is that I don't want to decorate methods one by one, some classes they need to be decorated by a same decorator, Is there any solution to fix it?
Your can start all method that required to be decorated with some prefix and then use something like this:
class Xobject(object):
def __init__(self, decorator):
for method_name in dir(self):
if method_name.startswith("dec_"):
attr = getattr(self, method_name)
wrapped = decorator(attr)
setattr(self, method_name, wrapped)
def dec_me_1(self):
print("In dec_me1")
return 0
def dec_me_2(self):
print("In dec_me2")
return 1
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
x = Xobject(decorator)
x.dec_me_1()
x.dec_me_2()
UPDATE:
You can decorate class by mean of function below. When using Python you should know that class in Python is also object so you could change it and pass it to the other function.
def decorator(func):
def wrapped(*args):
print("TEST")
return func(*args)
return wrapped
def decorate_object(p_object, decorator):
for method_name in dir(p_object):
if method_name.startswith("dec_"):
attr = getattr(p_object, method_name)
wrapped = decorator(attr)
setattr(p_object, method_name, wrapped)
decorate_object(Xobject, decorator)
x = Xobject()
x.dec_me_1()
x.dec_me_2()
Also your can decorate already instantiated object same way:
x = Xobject()
x.dec_me_1()
x.dec_me_2()
decorate_object(x, decorator)
x.dec_me_1()
x.dec_me_2()
I'm sure there are a few approaches to this, but the main leading options are:
Create a custom metaclass, where the __new__ method iterates across the attribute dictionary, identifies methods, and decorates them. See http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/ for an example of Python metaclass programming. Disadvantages: that may be more complex than we'd want to get into here.
Do the same in a regular class's __init__ method. Disadvantages: that only decorates instance methods and not class or static methods, and it's slower because it runs every time you create a new instance.
Do it outside the class:
class Foo(object):
def bar(self):
print 'bar'
for name, ref in vars(Foo):
if callable(ref): ...
Disadvantages: You only get one chance to do it right: at import time. Subclasses don't get modified.
Do it in a class-level decorator. Same disadvantages as doing it outside the class (I think).
At some point you have to be explicit about what gets wrapped and what doesn't.
If I've understood you correctly, I think you could do something like this:
def wrapper(func):
def inner(*args, **kwargs):
print "%s was called" func.__name__
return func(*args, **kwargs)
return inner
class A(object):
def foo(self):
print "foo called"
def bar(self):
print "BAR CALLED"
class B(A):
#wrapper
def foo(self):
super(B, self).foo()
class C(A):
#wrapper
def bar(self):
super(C, self).bar()
Stick = A()
Dave = B()
Jupiter = C()
Jupiter.foo() #prints "foo called"
Jupiter.bar() #prints "bar wrapped" and "BAR CALLED"