PEP-542: Dot Notation Assignment In Function Header (#205)
* PEP-542 initial commit * Fixes and improvements based on pre-feedback * Fix typo
This commit is contained in:
parent
fa86704be3
commit
38a49f70d8
|
@ -0,0 +1,161 @@
|
|||
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.
|
Loading…
Reference in New Issue