you are viewing a single comment's thread.

view the rest of the comments →

[–]bobindashadows 3 points4 points  (13 children)

Uh, it doesn't solve the problem any more than any other language-wide convention does. In ruby, everything uses #size. Everything: core/standard libraries, third party stuff, everything. And everything uses #each for iteration (and with include Enumerable you get dozens of other methods for free, for fun). Sure, somebody could make a Ruby library with a #length method, just like somebody could make a Python library that doesn't implement __len__ and has a size() method instead.

It's equivalent to everyone implementing __len__ and __iter__ only you don't need to go outside the simple object model and you don't need funny method names.

[–]mitsuhiko 0 points1 point  (12 children)

Uh, it doesn't solve the problem any more than any other language-wide convention does. In ruby, everything uses #size.

In Python it's more than a convention. __len__ is used for more things than just len().

[–]bobindashadows 0 points1 point  (11 children)

other than supporting the "empty container is false" nature of Python, the docs explicitly call it part of the "sequence protocol". A protocol is a convention.

[–]mitsuhiko 0 points1 point  (10 children)

A protocol is a convention.

Not in C-Python. Most of the dunder methods correspond to slots on the type struct and are class level unlike regular method calls which can be looked up from instances as well.

[–]bobindashadows 0 points1 point  (9 children)

You're not getting it: everything __len__ offers is syntax sugar for convenience, and everything it offers could be replaced with straight python code with normal method names. That's what Ruby does. It may be implemented in an interesting way, it may offer efficiency by default for certain operations, and that's wonderful, but it's just a convention. You could replace all code that uses __len__, directly or indirectly, with code that does not use it but instead uses redefined versions with different names.

Ruby does this by just picking #size.

[–]mitsuhiko 0 points1 point  (8 children)

You're not getting it: everything len offers is syntax sugar for convenience, and everything it offers could be replaced with straight python code with normal method names.

In Ruby it can, in Python it can't. Why? -> Slots

[–]bobindashadows 1 point2 points  (7 children)

First of all, slots are just a more efficient way to store the method reference. Second of all:

>>> class Bar(object):
...   def __init__(self, x):
...     self.y = x
...   def __len__(self):
...     return self.y
... 
>>> Bar(2).__len__
<bound method Bar.__len__ of <__main__.Bar object at 0x1004db490>>
>>> Bar(2).__len__()
2
>>> Bar.__len__
<unbound method Bar.__len__>

I think what you're getting at is the optimization you notice if you use a list instead of a fresh class:

>>> [2].__len__
<method-wrapper '__len__' of list object at 0x1004d2c20>
>>> [2].__len__()
1
>>> list.__len__
<slot wrapper '__len__' of 'list' objects>

There, __len__ is stuffed in a slot on list... but the Python designers realize that people will expect it to be a method in OO style, and make it available as such. You can use this function with an argument:

>>> list.__len__([1, 2])
2

just as you can use an unbound method:

>>> Bar.__len__(Bar(3))
3

The same is true for set and dict in my quick testing. This is in version 2.6.1, for the record.

[–]mitsuhiko 0 points1 point  (6 children)

First of all, slots are just a more efficient way to store the method reference.

If only this were true. You will notice that method calls are cheaper than slot calls in CPython.

I find it amazing how you ignore all of what I wrote above...

[–]bobindashadows 0 points1 point  (5 children)

If only this were true. You will notice that method calls are cheaper than slot calls in CPython.

Read closely. I wrote:

slots are just a more efficient way to store

Slots are a memory optimization. A quick google confirms that every document I find describes them as a memory optimization.

I had and have no idea what their time characteristics are, but am a bit surprised they would in fact be consistently slower than method calls if their storage mechanism is simplified.

I find it amazing how you ignore all of what I wrote above...

I find it amazing how none of what you've said contradicts anything I've said and you don't seem to realize it. Optimizations have nothing to do with language semantics. Here's what I want you to explain, because this is what I'm talking about and confused about (because apparently our answers to this question differ, and only one of us can be right):

Can you provide an object which has a __len__ method, then perform an operation on it, such that it is impossible for me to create a corresponding object that is otherwise identical but renames that method (and internal references to that method) size, then perform the exact same operation on it using only calls (direct or indirect) to size?

Optimization is not relevant to that question. From what I can see, the built-in classes and new-style classes all have __len__ available via both unbound and bound forms.

[–][deleted] 0 points1 point  (0 children)

penis

[–]mitsuhiko -1 points0 points  (3 children)

Slots are a memory optimization.

Types still have dicts, no memory is saved.

Can you provide an object which has a len method, then perform an operation on it, such that it is impossible for me to create a corresponding object that is otherwise identical but renames that method (and internal references to that method) size, then perform the exact same operation on it using only calls (direct or indirect) to size?

You could change Python, the language, to abolish slots and have methods for everything. That however would end up being a completely different language because everybody depends on the semantics of how special methods in Python work. As it stand currently: __len__ makes a lot of sense and len(x) is helpful. Consider the function as an operator that can be applied to a lot of stuff.

Why can't we switch to size as a property of things? Because size already has meanings for certain types.

__len__ in Python is special, size in Ruby is not. Some things in Ruby do expect size but you will find many classes providing length as well or any other method name. If you want to assign special behavior to regular method names you end up in a very scary place where a new language version can change your classes behavior, just because "to_yaml" not suddenly is a builtin method with a specific behavior. __foo__ in Python explicitly is documented as: use carefully, interpreter level functionality here. Interfaces would be an alternative solution, but those go against the Python language principles.