I've just come across Python decorators. Just out of interest, can you apply your own decorator to a built-in object method somehow? Say I wanted to apply this:
def remove_empty(fn):
def filtered():
return filter(lambda x: x != '', fn())
return filtered
To this:
some_string.split('\n')
in order to remove empty strings. Is it possible? Or even a good idea?
To create a decorator function in Python, I create an outer function that takes a function as an argument. There is also an inner function that wraps around the decorated function. To use a decorator ,you attach it to a function like you see in the code below.
Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together. It is also known as nested decorators in Python.
Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.
To decorate a method in a class, first use the '@' symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function.
It's possible in a sense; it depends on what exactly you mean. Decorator syntax like this...
@dec
def foo():
pass
is really just sugar for this:
def foo():
pass
foo = dec(foo)
So there's nothing to stop you from using a decorator on a predefined function in the global namespace.
func = dec(func)
But the methods of built-in classes live in the namespace of that class, and that namespace can't be modified directly, as chepner has already pointed out. That's a good thing, because it ensures that objects of type str
will behave as expected! However, you could subclass str and decorate the method that way. (The below works in Python 2; in Python 3, pass the output of filter
to a list. super
also may work a little differently; I'll post a Python 3 update in the future.)
>>> def remove_empty(fn):
... def filtered(*args, **kwargs):
... return filter(lambda x: x != '', fn(*args, **kwargs))
... return filtered
...
>>> class WeirdString(str):
... @remove_empty
... def split(self, *args, **kwargs):
... return super(WeirdString, self).split(*args, **kwargs)
...
>>> 'This decorator is unnecessary\n\n\n'.split('\n')
['This decorator is unnecessary', '', '', '']
>>> WeirdString('This decorator is unnecessary\n\n\n').split('\n')
['This decorator is unnecessary']
Or more directly (and so more in the spirit of decorator use):
>>> class WeirdString2(str):
... split = remove_empty(str.split)
...
>>> WeirdString2('This decorator is unnecessary\n\n\n').split('\n')
['This decorator is unnecessary']
In the case of this particular example, I'd prefer an explicit filter. But I can imagine, for example, a subclass of a built-in class that does some memoization or something like that.
I'm afraid the answer is no. Decorators are applied when the function is defined, and str.split
is pre-defined. You might think you could do something explicit like
str.split = remove_empty(str.split)
but that is not permitted:
Traceback (most recent call last):
File "tmp.py", line 8, in <module>
str.split = remove_empty(str.split)
TypeError: can't set attributes of built-in/extension type 'str'
Of course it is. Just write
remove_empty(lambda: some_string.split('\n'))()
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