Define a method or function attribute outside of a class with the dot operator

I'm suggesting the addition of support to using a dot notation when defining a function to be a method of a class, or a callback attribute. For example: def foo(self): pass Foo.foo = foo Becomes: def Foo.foo(self): pass Other syntaxes can also be used if the dot itself is an issue, although I dislike these: def Foo:foo(self): def foo@Foo(self): def Foo>foo(self): def Foo&foo(self): This functionality would be useful in the few rare cases where the class itself needs to be accessed in the function's definition (decorator, typing, etc.): @barify(Foo) def Foo.method(self, other: Foo) -> Foo: pass And when an object needs a callback as an attribute: class Menu: def __init__(self, items=None, select_callback=None): self.items = items if items is not None else [] self.select_callback = select_callback my_menu = Menu([item1, item2]) def my_menu.select_callback(self, item_index): print(self.items[item_index]) As opposed to: my_menu = Menu([item1, item2]) def select_callback(self, item_index): print(self.items[item_index]) my_menu.select_callback = select_callback Or defining them in "unnatural" order: def select_callback(self, item_index): print(self.items[item_index]) my_menu = Menu([item1, item2], select_callback)

On Fri, Feb 10, 2017 at 11:13:47AM +0200, Markus Meskanen wrote:
That saves one line, at the cost of introducing even more complexity to the language. Are these use-cases common enough to justify the extra syntax?
That's just putting arbitrary symbols into the statement. I know that *ultimately* all symbols are arbitrary, but . dot means attribute access in Python, so Foo.foo at least has some connection to creating an attribute of Foo called foo. In what way does "Foo greater than foo" suggest to the reader that foo ends up as an attribute of Foo?
I'm sure that there are a number of use-cases for injecting a method into an existing class. But we already have a way of doing that: def method(self): ... Spam.method = method What advantage does this proposed syntax have? Since that's not actually a rhetorical question, I'll answer it myself: def Spam.method(self) not only saves the line Spam.method = method but it also avoids leaving a global name "method" in the namespace (no need to follow with `del method`); it makes it explicit from the beginning that the function is an attribute of Spam. If the implementation is clever enough, it can avoid clashing with a global of the same name: eggs = "something" def Spam.eggs(self): ... def Cheese.eggs(self): ... assert eggs == "something" So the idea isn't without merit; but the question in my mind is whether the advantages outweigh the extra complexity. -- Steve

Hi Markus, Thanks for writing this up, as I've had this same very valid problem before. On 10 February 2017 at 10:13, Markus Meskanen <markusmeskanen@gmail.com> wrote:
I'm suggesting the addition of support to using a dot notation when defining a function to be a method of a class, or a callback attribute.
Your solution to me seems like a 'hack': class monkey-patching during runtime is already available if you really need it, and your proposal only makes it easier, which I don't really like.
This problem could just as well be solved by allowing access to a scope-level variable (__class__? __type__?) which is available in the class body at construction time, which points to the (eventual) class type object, or evaluating the type hints in a class only after the class is created, which then allows for that class to be pointed to in the type annotations. E.G. this does not work right now: class A: def foo(self: A): pass as it fails with NameError: A is not defined, whereas you'd expect it to work. The problem is very annoying when you're trying to work with the dunder methods for e.g. numerical objects, as you cannot say '+ is only allowed for these types', but it is not limited to this scope only. -Matthias

On Fri, Feb 10, 2017 at 12:29 PM, Stephan Houben <stephanh42@gmail.com> wrote:
This would work, but I still believe the proposed method is much shorter and easier to follow. Decorator approach is no different from doing `Foo.bar = bar` under the function definition I think, except it requires one to figure out what the decorator does first.

On 2/10/2017 4:13 AM, Markus Meskanen wrote:
I'm suggesting the addition of support to using a dot notation when defining a function to be a method of a class, or a callback attribute.
My default starting position for every proposed syntax addition is -1. 1. Additions usually make Python more complicated and harder to learn. (This can be argued in particular cases.) 2. Syntax changes, when used, cut code off from past versions. Consequently, to me, there should be a non-trivial gain to compensate for the above.
Saving about 10 keystrokes is close to trivial. If Foo is defined in the same file, then putting the def in the class statement would save even more. I am not enthusiastic about enablin the following style of class definition. class Foo: "Object that implement the Foo protocol. def Foo.__init__(self, x): self.x = s def Foo.__getitem__(self, key): return vars(Foo)[key] ...
A function with a 'self' parameter is normally an instance method (a function attribute of the class). As an instance attribute, it will have be called as inst.select_callback(inst, index). But I suppose you could find an example function that I like better as an instance attribute.
or my_menu.select_callback = (lambda self, item_index: print(self.items[item_index])) The problem with two-phase initialization is that one temporarily has a partially initialized and likely useless object. I am not enthusiastic about encouraging this.
Or defining them in "unnatural" order:
To me, this is the natural and proper order: create all the objects needed to initialize an instance before creating it. When __init__ returns, the instance is ready to go. In tkinter programming, callbacks must be defined before they are used in a bind or after call, which passes them on to tk, where they are not directly accessible as attributes.
Looks good to me ;-) -- Terry Jan Reedy

On Fri, Feb 10, 2017 at 09:05:49PM -0500, Terry Reedy wrote:
The same argument can be made for @decorator syntax. And, if I understand correctly, the same argument *was* made against decorator syntax: that it was trivial, unnecessary and confusing. In a realistic example, the function definition line: def method(self, arg): and the line which actually injects it into the class: MyClass.method = method may be separated by *many lines of code*. Yes, short methods are better than long methods, but even so, sometimes you have a long method. Arguably the most important part of the definition of this method (that it belongs to MyClass) may be lost way down the end, past the implementation. Just as with decorators, the current idiom for method injecting repeats the method name too many times: # the bad old way of using decorators def function(): ... ... ... ... function = decorator(function) # the nice clean way @decorator def function(): ... ... ... ... Likewise this proposal: # the bad current way repeats the method name at least three times: def method(self): ... ... ... ... MyClass.method = method del method # and for injecting into an instance, the instance twice: def method(self): ... ... ... ... instance.method = types.MethodType(method, instance) del method # the clean proposed way: def instance.method(self): # or MyClass.method ... ... ... ... We can reasonably argue that the benefit is not worth the cost; we can even reasonably argue that we don't wish to encourage the Interceptor design pattern, method swizzling, extension methods or method injection, whatever name you want to call it. Even monkey-patching. But I don't think that we can reasonably argue that the suggested syntax isn't a clear, non-trivial win over the status quo, not unless we're also going to argue that introducing decorator syntax was a waste of time.
Neither am I, but why would anyone write classes that way? Do you have evidence that people in the C#, VB, Objective-C, Swift or Lua communities routinely do that?
Indeed. But in practice, I don't believe that two-phase initialization is the usual reason for using method injection. It seems to be more commonly used for "introspection, overriding default behavior, or maybe even dynamic method loading". https://blog.newrelic.com/2014/04/16/right-way-to-swizzle/ -- Steve

On Mon, Feb 13, 2017 at 5:48 PM, Steven D'Aprano <steve@pearwood.info> wrote:
I argue it's not a win specifically because we HAVE decorators already. My off-the-cuff `@attach_to(thing)` decorator factory missed some details one would want, I now recognize. We might want to attach a `.__class__` attribute, futz with `.__qualname__` and a few other things Nick points to. You've been spelling this as `@inject_method(Class)`. Apart from the name of the decorator, the idea of having a *uniform* syntax for "put the decoration" at the top because the function might be long" is an advantage. I agree that decorators are just syntax sugar, of course. But the new syntax sugar is just *too duplicative* of that we already have. Moreover, I think your spelling of what it is sugar for is slightly off. The `del method` at the end feels wrong to me. Specifically, in the example I repeated of attaching callbacks, the reason I'd want a function defined outside any particular class (or instance) scope is because I might want to use the same function as a method of various classes. An `attach_to()` decorator can leave the defined function untouched, and only muck with the version that gets attached. Hence you can even chain decorators: @attach_to(this_menu) @attach_to(that_menu) def callback(self, ...): ... Then later on: yet_another_menu.callback = callback -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

On Mon, Feb 13, 2017 at 06:14:34PM -0800, David Mertz wrote:
I wouldn't word it quite like that, but I'll certainly agree that a decorator may be able to (almost) solve the method-injection use-case. Being able to write: @inject(Class) def method ... is almost as nice as def Class.method ... The one thing a decorator can't do is clean up after itself and avoid leaving an unneeded and unnecessary 'method' name in the current namespace. [...]
The existing syntax won't be going away, so anything you can do now, you will still be able to do :-) My gut feeling is that `def Class.method` should avoid polluting the current namespace. If you need access to the method again, you can always grab it from `Class.method`. -- Steve

On Feb 14, 2017, at 12:48 PM, Steven D'Aprano wrote:
Well, not exactly. Remember that the semantics, and common decorators like property, existed well before the decorator syntax was added. We had a lot of experience writing post-definition "decorators", which taught us that the behavior was useful but the syntax was painful. And adding the syntax made a huge improvement in readability. Cheers, -Barry

On Tue, Feb 14, 2017 at 04:34:09PM -0500, Barry Warsaw wrote:
Your memory of the discussion may be more accurate than mine, but the PEP does suggest that general unfamiliarity with the concept was still a large issue: https://www.python.org/dev/peps/pep-0318/#why-is-this-so-hard -- Steve

On Fri, Feb 10, 2017 at 11:13:47AM +0200, Markus Meskanen wrote:
That saves one line, at the cost of introducing even more complexity to the language. Are these use-cases common enough to justify the extra syntax?
That's just putting arbitrary symbols into the statement. I know that *ultimately* all symbols are arbitrary, but . dot means attribute access in Python, so Foo.foo at least has some connection to creating an attribute of Foo called foo. In what way does "Foo greater than foo" suggest to the reader that foo ends up as an attribute of Foo?
I'm sure that there are a number of use-cases for injecting a method into an existing class. But we already have a way of doing that: def method(self): ... Spam.method = method What advantage does this proposed syntax have? Since that's not actually a rhetorical question, I'll answer it myself: def Spam.method(self) not only saves the line Spam.method = method but it also avoids leaving a global name "method" in the namespace (no need to follow with `del method`); it makes it explicit from the beginning that the function is an attribute of Spam. If the implementation is clever enough, it can avoid clashing with a global of the same name: eggs = "something" def Spam.eggs(self): ... def Cheese.eggs(self): ... assert eggs == "something" So the idea isn't without merit; but the question in my mind is whether the advantages outweigh the extra complexity. -- Steve

Hi Markus, Thanks for writing this up, as I've had this same very valid problem before. On 10 February 2017 at 10:13, Markus Meskanen <markusmeskanen@gmail.com> wrote:
I'm suggesting the addition of support to using a dot notation when defining a function to be a method of a class, or a callback attribute.
Your solution to me seems like a 'hack': class monkey-patching during runtime is already available if you really need it, and your proposal only makes it easier, which I don't really like.
This problem could just as well be solved by allowing access to a scope-level variable (__class__? __type__?) which is available in the class body at construction time, which points to the (eventual) class type object, or evaluating the type hints in a class only after the class is created, which then allows for that class to be pointed to in the type annotations. E.G. this does not work right now: class A: def foo(self: A): pass as it fails with NameError: A is not defined, whereas you'd expect it to work. The problem is very annoying when you're trying to work with the dunder methods for e.g. numerical objects, as you cannot say '+ is only allowed for these types', but it is not limited to this scope only. -Matthias

On Fri, Feb 10, 2017 at 12:29 PM, Stephan Houben <stephanh42@gmail.com> wrote:
This would work, but I still believe the proposed method is much shorter and easier to follow. Decorator approach is no different from doing `Foo.bar = bar` under the function definition I think, except it requires one to figure out what the decorator does first.

On 2/10/2017 4:13 AM, Markus Meskanen wrote:
I'm suggesting the addition of support to using a dot notation when defining a function to be a method of a class, or a callback attribute.
My default starting position for every proposed syntax addition is -1. 1. Additions usually make Python more complicated and harder to learn. (This can be argued in particular cases.) 2. Syntax changes, when used, cut code off from past versions. Consequently, to me, there should be a non-trivial gain to compensate for the above.
Saving about 10 keystrokes is close to trivial. If Foo is defined in the same file, then putting the def in the class statement would save even more. I am not enthusiastic about enablin the following style of class definition. class Foo: "Object that implement the Foo protocol. def Foo.__init__(self, x): self.x = s def Foo.__getitem__(self, key): return vars(Foo)[key] ...
A function with a 'self' parameter is normally an instance method (a function attribute of the class). As an instance attribute, it will have be called as inst.select_callback(inst, index). But I suppose you could find an example function that I like better as an instance attribute.
or my_menu.select_callback = (lambda self, item_index: print(self.items[item_index])) The problem with two-phase initialization is that one temporarily has a partially initialized and likely useless object. I am not enthusiastic about encouraging this.
Or defining them in "unnatural" order:
To me, this is the natural and proper order: create all the objects needed to initialize an instance before creating it. When __init__ returns, the instance is ready to go. In tkinter programming, callbacks must be defined before they are used in a bind or after call, which passes them on to tk, where they are not directly accessible as attributes.
Looks good to me ;-) -- Terry Jan Reedy

On Fri, Feb 10, 2017 at 09:05:49PM -0500, Terry Reedy wrote:
The same argument can be made for @decorator syntax. And, if I understand correctly, the same argument *was* made against decorator syntax: that it was trivial, unnecessary and confusing. In a realistic example, the function definition line: def method(self, arg): and the line which actually injects it into the class: MyClass.method = method may be separated by *many lines of code*. Yes, short methods are better than long methods, but even so, sometimes you have a long method. Arguably the most important part of the definition of this method (that it belongs to MyClass) may be lost way down the end, past the implementation. Just as with decorators, the current idiom for method injecting repeats the method name too many times: # the bad old way of using decorators def function(): ... ... ... ... function = decorator(function) # the nice clean way @decorator def function(): ... ... ... ... Likewise this proposal: # the bad current way repeats the method name at least three times: def method(self): ... ... ... ... MyClass.method = method del method # and for injecting into an instance, the instance twice: def method(self): ... ... ... ... instance.method = types.MethodType(method, instance) del method # the clean proposed way: def instance.method(self): # or MyClass.method ... ... ... ... We can reasonably argue that the benefit is not worth the cost; we can even reasonably argue that we don't wish to encourage the Interceptor design pattern, method swizzling, extension methods or method injection, whatever name you want to call it. Even monkey-patching. But I don't think that we can reasonably argue that the suggested syntax isn't a clear, non-trivial win over the status quo, not unless we're also going to argue that introducing decorator syntax was a waste of time.
Neither am I, but why would anyone write classes that way? Do you have evidence that people in the C#, VB, Objective-C, Swift or Lua communities routinely do that?
Indeed. But in practice, I don't believe that two-phase initialization is the usual reason for using method injection. It seems to be more commonly used for "introspection, overriding default behavior, or maybe even dynamic method loading". https://blog.newrelic.com/2014/04/16/right-way-to-swizzle/ -- Steve

On Mon, Feb 13, 2017 at 5:48 PM, Steven D'Aprano <steve@pearwood.info> wrote:
I argue it's not a win specifically because we HAVE decorators already. My off-the-cuff `@attach_to(thing)` decorator factory missed some details one would want, I now recognize. We might want to attach a `.__class__` attribute, futz with `.__qualname__` and a few other things Nick points to. You've been spelling this as `@inject_method(Class)`. Apart from the name of the decorator, the idea of having a *uniform* syntax for "put the decoration" at the top because the function might be long" is an advantage. I agree that decorators are just syntax sugar, of course. But the new syntax sugar is just *too duplicative* of that we already have. Moreover, I think your spelling of what it is sugar for is slightly off. The `del method` at the end feels wrong to me. Specifically, in the example I repeated of attaching callbacks, the reason I'd want a function defined outside any particular class (or instance) scope is because I might want to use the same function as a method of various classes. An `attach_to()` decorator can leave the defined function untouched, and only muck with the version that gets attached. Hence you can even chain decorators: @attach_to(this_menu) @attach_to(that_menu) def callback(self, ...): ... Then later on: yet_another_menu.callback = callback -- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.

On Mon, Feb 13, 2017 at 06:14:34PM -0800, David Mertz wrote:
I wouldn't word it quite like that, but I'll certainly agree that a decorator may be able to (almost) solve the method-injection use-case. Being able to write: @inject(Class) def method ... is almost as nice as def Class.method ... The one thing a decorator can't do is clean up after itself and avoid leaving an unneeded and unnecessary 'method' name in the current namespace. [...]
The existing syntax won't be going away, so anything you can do now, you will still be able to do :-) My gut feeling is that `def Class.method` should avoid polluting the current namespace. If you need access to the method again, you can always grab it from `Class.method`. -- Steve

On Feb 14, 2017, at 12:48 PM, Steven D'Aprano wrote:
Well, not exactly. Remember that the semantics, and common decorators like property, existed well before the decorator syntax was added. We had a lot of experience writing post-definition "decorators", which taught us that the behavior was useful but the syntax was painful. And adding the syntax made a huge improvement in readability. Cheers, -Barry

On Tue, Feb 14, 2017 at 04:34:09PM -0500, Barry Warsaw wrote:
Your memory of the discussion may be more accurate than mine, but the PEP does suggest that general unfamiliarity with the concept was still a large issue: https://www.python.org/dev/peps/pep-0318/#why-is-this-so-hard -- Steve
participants (8)
-
Barry Warsaw
-
David Mertz
-
Kyle Lahnakoski
-
Markus Meskanen
-
Matthias welp
-
Stephan Houben
-
Steven D'Aprano
-
Terry Reedy