162 lines
3.6 KiB
Plaintext
162 lines
3.6 KiB
Plaintext
|
PEP: 542
|
||
|
Title: Dot Notation Assignment In Function Header
|
||
|
Version: $Revision$
|
||
|
Last-Modified: $Date$
|
||
|
Author: Markus Meskanen <markusmeskanen@gmail.com>
|
||
|
Status: Draft
|
||
|
Type: Standards Track
|
||
|
Content-Type: text/x-rst
|
||
|
Created: 10-February-2017
|
||
|
|
||
|
|
||
|
Abstract
|
||
|
========
|
||
|
|
||
|
Function definitions only allow simple function names to be used,
|
||
|
even though functions are assignable first class objects.
|
||
|
|
||
|
This PEP proposes adding support for assigning a function to
|
||
|
a class or instance attribute directly in the function
|
||
|
definition's header by using the dot notation to separate
|
||
|
the object from the function's name.
|
||
|
|
||
|
Although a similar feature, this PEP does not address general
|
||
|
assignment to anything that supports assignment, such as dict keys
|
||
|
and list indexes.
|
||
|
|
||
|
Rationale
|
||
|
=========
|
||
|
|
||
|
Currently if a function needs to be assigned to a class or instance
|
||
|
attribute, it requires an additional assignment statement to be made::
|
||
|
|
||
|
class MyClass:
|
||
|
...
|
||
|
|
||
|
my_instance = MyClass()
|
||
|
|
||
|
def my_function(self):
|
||
|
...
|
||
|
|
||
|
# Assign to class attribute
|
||
|
MyClass.my_function = my_function
|
||
|
|
||
|
# Or assign to instance attribtue
|
||
|
my_instance.my_function = my_function
|
||
|
|
||
|
While this isn't usually an inconvenience, using dot notation to
|
||
|
assign directly in the function's header would greatly simplify this::
|
||
|
|
||
|
class MyClass:
|
||
|
...
|
||
|
|
||
|
my_instance = MyClass()
|
||
|
|
||
|
# Assign to class attribute
|
||
|
def MyClass.my_function(self):
|
||
|
...
|
||
|
|
||
|
# Or assign to instance attribute
|
||
|
def my_instance.my_function(self):
|
||
|
...
|
||
|
|
||
|
There are multiple reasons to use this functionality over
|
||
|
a standard class method, for example when the class is referenced
|
||
|
inside the function's header (such as with decorators and typing).
|
||
|
This is also useful when an instance requires a callback 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(item_index, menu):
|
||
|
print(menu.items[item_index])
|
||
|
|
||
|
As opposed to::
|
||
|
|
||
|
my_menu = Menu([item1, item2])
|
||
|
|
||
|
def select_callback(item_index, menu):
|
||
|
print(menu.items[item_index])
|
||
|
my_menu.select_callback = select_callback
|
||
|
|
||
|
Or defining them in an "unnatural" order::
|
||
|
|
||
|
def select_callback(item_index, menu):
|
||
|
print(menu.items[item_index])
|
||
|
|
||
|
my_menu = Menu([item1, item2], select_callback)
|
||
|
|
||
|
It reads better than the "unnatural" way, since you already know at
|
||
|
the time of the function definition what it's goig to be used for.
|
||
|
It also saves one line of code while removing visual complexity.
|
||
|
|
||
|
The feature would also avoid leaving the function's name into
|
||
|
the global namespace::
|
||
|
|
||
|
eggs = 'something'
|
||
|
|
||
|
def Spam.eggs(self):
|
||
|
...
|
||
|
|
||
|
def Cheese.eggs(self):
|
||
|
...
|
||
|
|
||
|
assert eggs == 'something'
|
||
|
|
||
|
Ideally this would be just syntastic sugar::
|
||
|
|
||
|
def x.y():
|
||
|
...
|
||
|
|
||
|
# Equals to
|
||
|
|
||
|
def y():
|
||
|
...
|
||
|
x.y = y
|
||
|
|
||
|
Similar to how decorators are syntastic sugar::
|
||
|
|
||
|
@decorate
|
||
|
def f():
|
||
|
...
|
||
|
|
||
|
# Equals to
|
||
|
|
||
|
def f():
|
||
|
...
|
||
|
f = decorate(f)
|
||
|
|
||
|
Implementation
|
||
|
==============
|
||
|
|
||
|
The `__name__` would follow the principles of a normal function::
|
||
|
|
||
|
class MyClass:
|
||
|
def my_function1(self):
|
||
|
...
|
||
|
|
||
|
def MyClass.my_function2(self):
|
||
|
...
|
||
|
|
||
|
assert my_function1.__name__ == 'my_function1'
|
||
|
assert my_function2.__name__ == 'my_function2'
|
||
|
|
||
|
The grammar would use `dotted_name` to support chaining of attributes::
|
||
|
|
||
|
def Person.name.fset(self, value):
|
||
|
self._name = value
|
||
|
|
||
|
Backwards Compatibility
|
||
|
=======================
|
||
|
|
||
|
This PEP is fully backwards compatible.
|
||
|
|
||
|
Copyright
|
||
|
=========
|
||
|
|
||
|
This document has been placed in the public domain.
|