7 ms·
It feels a bit hampered by "bad" design decisions to me I think its safe to say that the current view of what decorators do is, a) filter things going to funct
by RubyPinch 10y ago
It feels a bit hampered by "bad" design decisions to me
I think its safe to say that the current view of what decorators do is, a) filter things going to functions (so they error, or hit a cache), or b) register a function with another library (e.g. flask path decorator)
This library does a lot more than that, so it seems that maybe a metaclass (which exists to mutate the class's creation) would be more appropriate?
This seems to act as a __init__ replacement, but it would be cool to act as a __init__ assistant (in other words, run the attr.s __init__, then run the user defined __init__)
The ordering of attrs matters a bit more than one would expect, not only does it define the attribute order for creating a new instance, it also defines the order of attributes when sorting against other classes. You would not want a id=attr.ib() at the top of the class definition, where it most makes sense!
The ordering of attrs is done through the .ib() call itself (which gives itself a number, and increments a global counter). This is a way of doing it, and practically might be the best way (it won't lead to any inconsistencies within a class), but it still feels weird
I personally won't use it, but I might make a competitor to it for personal use
- nbadg 10y agoAgreed. From the post, it seems to me like another (IMO better) alternative to this awkward attr-specific convention and syntax etc would be to create a factory function for dynamic class definition, in a similar vein to namedtuples. This is functionally similar to the metaclass route, and to be honest I'm not sure what the tradeoffs would be. Either way, I think metaprogramming is a much better approach than attrs if the problem you're trying to solve is "generic container classes take too long to get set up". I think, were I to write something like this, I would probably do it using a metaclass, and then inject an __init__ on the MRO between the programmer-defined __init__ and the actual super().__init__. That way, you could call super().__init__(arg1, arg2, arg3) for the automatic attribute setting. You could also hook this in to some metaclass-defined storage containers to automate the __repr__ implementation, etc.
- deleted 10y ago[deleted]
- wcummings 10y ago>I think its safe to say that the current view of what decorators do is, a) filter things going to functions (so they error, or hit a cache), or b) register a function with another library (e.g. flask path decorator) Why is it safe to say that? Aren't decorators just syntax for higher-order functions?
- RubyPinch 10y agoits "safe" to say that, like its also "safe" to say that <article> will be used to denote articles in HTML Yes, decorators can do whatever they want, but, in practical usage I'd say a majority of decorator usages do not mutate the class/function significantly from its original typed-out intent (like, they don't often add 8+ methods to a class for example, but of course they can) not literally "safe" to say.
- emidln 10y agoYour view of what decorators doesn't seem to reflect the usage I've experienced writing Python professionally over the last 10 years. Decorators take an object and return a object (by convention a callable, although this isn't enforced in the slightest). Maybe they modify a function or method signature, maybe it produces an unrelated object, or maybe one augments the object by adding side effects like caching or logging. There is a special syntax (@) legal for classes, functions, and methods that provides a shortcut for the common pattern of: def decorator(): ... def foo(): ... foo = decorator(foo) such that we can say: @decorator def foo(): ... Things I've seen decorators do: * add logging * add tracing * enable type validation * register functions/objects for various reasons * produce full objects out of functions * swap out implementations for global functions/vars * change output formats to json/xml/yaml * take a class, inspect a database schema and bind various internal attributes to update items in a row for that schema (somewhat common in ORMs) I don't see this attrs library as any more egregious than other uses. In particular, I don't see why mutating a type via a metaclass is preferable to using a decorator. I can offer that decorators generating objects compose much better than metaclasses do (at least they have in python 2.x, I haven't had a chance to use 3 professionally).
- RubyPinch 10y agoI know how decorators work and you might be missing the point I was making by a fair mile The following don't mutate the mental map of how the function works, its "this function is still what I typed": "add logging" "add tracing" "register functions/objects for various reasons" The following act as a filtering role, the inside isn't "changed", the mental map becomes "This function will operate within this feature in some way, but otherwise is still what I typed": "enable type validation" "change output formats to json/xml/yaml" "swap out implementations for global functions/vars" (assuming the implementations are comparable) The following is "What I've typed is not a proper representation, and some things may act differently than usual" e.g. things that can take lists can no longer, due to needing to be serializable (and the least common decorator case): "take a class, inspect a database schema and bind various internal attributes to update items in a row for that schema (somewhat common in ORMs)" (and that is usually implemented via a subclassing of a Model class, which then in turn can implement a metaclass, to get the full mutate-class-on-creation funs, iirc) - - - > In particular, I don't see why mutating a type via a metaclass is preferable to using a decorator. Its not a technical topic, its a semantics topic. while both can do whatever silliness they want, decorators are usually reserved for incidental tasks on the side, and wrapping the original function in some light filtering. Metaclasses are expected to do mutation to the class (hence requiring a metaclass), so semantically a subclass or metaclass would be more appropriate to use for this task, compared to a decorator (from the standard library, Enums come to mind as an example for this)
- jorge-fundido 10y agoWhile I view python metaclasses (metaprogramming in general) as a tool of last resort, I have to agree with you. I applaud the attempt - I think it fills an important gap between lists/dicts, namedtuples, and classes. Implemented as a metaclass, I think would be much simpler to get it accepted into core python with fewer backward compatibility issues.