Methods in Python are defined as functions in the class namespace. When
call the method of the object, the function will be called with the
object as the first argument. And furthermore, unbound methods can be
called with passing self as the first argument. For example,
str.upper('abc') returns 'ABC'. So class can be considered as a
namespace for functions related to objects of the specified type.
For methods defined in Python you can pass arbitrary object as self. But
in methods defined in C it should be an instance of the class in which
the method was defined. On one hand, it is very convenient -- you cab be
sure that self is binary compatible with the specified class. On other
hand, it restricts you.
I propose to add the METH_GENERAL flag, which is applicable to methods
as METH_CLASS and METH_STATIC (and is mutually incompatible with them).
If it is set, the check for the type of self will be omitted, and you
can pass an arbitrary object as the first argument of the unbound method.
I have several use cases for this.
1. Bytes and bytearray methods.
Bytes and bytearray has a lot of of sequence-like and string-like
methods. They also implement the buffer protocol. There are other
objects which implement the buffer protocol: memoryview, BytesIO,
array.array, mmap.mmap, ctypes arrays, NumPy arrays, but they lack most
of these methods. To use these methods (for example index()) you need to
copy the content to a bytes or bytearray, that invalidates the purpose
of the buffer protocol which was designed to avoid copying of binary
data. With general methods we can make bytes.index() be applicable to
any object which supports the buffer protocol.
bytes.index() and bytearray.index() will be equivalent, but the
difference between bytes.split() and bytearray.split() will be in the
result type.
2. Set methods.
Some set methods accept arbitrary number of arguments and accept not
only sets, but any iterables. Sou you can get a union of two lists for
example:
>>> set().union([1, 2], [2, 3])
{1, 2, 3}
It does work because a union with an empty set is a no-op. This trick
does not work with other methods. You have to convert the first iterable
to set explicitly.
>>> set([1, 2]).symmetric_difference([2, 3])
{1, 3}
With general methods we can make unbound set methods accepting arbitrary
iterables and convert the first one to set implicitly (or avoid creating
a set if it is possible). We could use set.symmetric_difference([1, 2],
[2, 3]).
Maybe there are other use cases. But I do not suggest making all methods
of builtin types general: only if there is a more general protocol (as
the buffer protocol or the iterator protocol) and there is a profit of
using such protocol without creating an object of the corresponding type
explicitly. For example, I think that str methods should not be general,
despite the fact that any object can be converted to string. Implicit
conversions to str does not have profit and may hide bugs. Other example
-- float.as_integer_ratio() should not accept int, despite the fact that
most function which accept float implicitly convert int to float. It is
a lossy conversion for large integers, and the loss will affect the result.
I used the term "general methods" to name this feature, but if there are
better proposition I will use it.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/Z4JVVNINPRY4RA7ZFOYOTOVZEVGE7DAZ/
Code of Conduct: http://python.org/psf/codeofconduct/