I am trying to write a decorator that gets a single arg, ie
@Printer(1)
def f():
print 3
So, naively, I tried:
class Printer:
def __init__(self,num):
self.__num=num
def __call__(self,func):
def wrapped(*args,**kargs):
print self.__num
return func(*args,**kargs**)
return wrapped
This is ok, but it also works as a decorator receiving no args, ie
@Printer
def a():
print 3
How can I prevent that?
Well, it's already effectively prevented, in the sense that calling a()
doesn't work.
But to stop it as the function is defined, I suppose you'd have to change __init__
to check the type of num
:
def __init__(self,num):
if callable(num):
raise TypeError('Printer decorator takes an argument')
self.__num=num
I don't know if this is really worth the bother, though. It already doesn't work as-is; you're really asking to enforce the types of arguments in a duck-typed language.
Are you sure it works without arguments? If I leave them out I get this error message:
Traceback (most recent call last): File "/tmp/blah.py", line 28, in ? a() TypeError: __call__() takes exactly 2 arguments (1 given)
You could try this alternative definition, though, if the class-based one doesn't work for you.
def Printer(num):
def wrapper(func):
def wrapped(*args, **kwargs):
print num
return func(*args, **kwargs)
return wrapped
return wrapper
The decorator is whatever the expression after @
evaluates to. In the first case, that's an instance of Printer
, so what happens is (pretty much) equivalent to
decorator = Printer(1) # an instance of Printer, the "1" is given to __init__
def f():
print 3
f = decorator(f) # == dec.__call__(f) , so in the end f is "wrapped"
In the second case, that's the class Printer, so you have
decorator = Printer # the class
def a():
print 3
a = decorator(a) # == Printer(a), so a is an instance of Printer
So, even though it works (because the constructor of Printer
takes one extra argument, just like __call__
), it's a totally different thing.
The python way of preventing this usually is: Don't do it. Make it clear (eg in the docstring) how the decorator works, and then trust that people do the right thing.
If you really want the check, Eevee's answer provides a way to catch this mistake (at runtime, of course---it's Python).
I can't think of an ideal answer, but if you force the Printer class to be instantiated with a keyword argument, it can never try to instantiate via the decorator itself, since that only deals with non-keyword arguments:
def __init__(self,**kwargs):
self.__num=kwargs["num"]
...
@Printer(num=1)
def a():
print 3
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.