Talk Notes: On The Use and Misuse of Decorators
I gave the talk On The Use and Misuse of Decorators as part of PyConline AU 2021, the second in annoyingly long sequence of not-in-person PyCon AU events. Here’s some code samples that you might be interested in:
Simple @property
implementation
This shows a demo of @property
-style getters. Setters are left as an exercise :)
def demo_property(f):
f.is_a_property = True
return f
class HasProperties:
def __getattribute__(self, name):
ret = super().__getattribute__(name)
if hasattr(ret, "is_a_property"):
return ret()
else:
return ret
class Demo(HasProperties):
@demo_property
def is_a_property(self):
return "I'm a property"
def is_a_function(self):
return "I'm a function"
a = Demo()
print(a.is_a_function())
print(a.is_a_property)
@run
(The Scoped Block)
@run
is a decorator that will run the body of the decorated function, and then store the result of that function in place of the function’s name. It makes it easier to assign the results of complex statements to a variable, and get the advantages of functions having less leaky scopes than if
or loop blocks.
def run(f):
return f()
@run
def hello_world():
return "Hello, World!"
print(hello_world)
@apply
(Multi-line stream transformers)
def apply(transformer, iterable_):
def _applicator(f):
return(transformer(f, iterable_))
return _applicator
@apply(map, range(100)
def fizzbuzzed(i):
if i % 3 == 0 and i % 5 == 0:
return "fizzbuzz"
if i % 3 == 0:
return "fizz"
elif i % 5 == 0:
return "buzz"
else:
return str(i)
Builders
def html(f):
builder = HtmlNodeBuilder("html")
f(builder)
return builder.build()
class HtmlNodeBuilder:
def __init__(self, tag_name):
self.tag_name = tag_name
self.nodes = []
def node(self, f):
builder = HtmlNodeBuilder(f.__name__)
f(builder)
self.nodes.append(builder.build())
def text(self, text):
self.nodes.append(text)
def build(self):
nodes = "\n".join(self.nodes)
return f"<{self.tag_name}>\n{nodes}\n</{self.tag_name}>"
@html
def document(b):
@b.node
def head(b):
@b.node
def title(b):
b.text("Hello, World!")
@b.node
def body(b):
for i in range(10, 0, -1):
@b.node
def p(b):
b.text(f"{i}")
Code Registries
This is an incomplete implementation of a code registry for handling simple text processing tasks:
```python
def register(self, input, output):
def _register_code(f):
self.registry[(input, output)] = f
return f
return _register_code
in_type = (iterable[str], (WILDCARD, ) out_type = (Counter, (WILDCARD, frequency))
@registry.register(in_type, out_type) def count_strings(strings):
return Counter(strings)
@registry.register( (iterable[str], (WILDCARD, )), (iterable[str], (WILDCARD, lowercase)) ) def words_to_lowercase(words): …
@registry.register( (iterable[str], (WILDCARD, )), (iterable[str], (WILDCARD, no_punctuation)) ) def words_without_punctuation(words): …
def find_steps( self, input_type, input_attrs, output_type, output_attrs ):
hand_wave()
def give_me(self, input, output_type, output_attrs):
steps = self.find_steps(
type(input), (), output_type, output_attrs
)
temp = input
for step in steps:
temp = step(temp)
return temp