[Python-Dev] type(obj) vs. obj.__class__

David Mertz mertz at gnosis.cx
Sun Oct 18 20:35:14 EDT 2015


I'm not sure what benchmark you used to define the speed of RingBuffer.
I'm sure you are reporting numbers accurately for your tests, but there are
"lies, damn lies, and benchmarks", so "how fast" has a lot of nuance to it.

In any case, redefining a method in a certain situation feels a lot less
magic to me than redefining .__class__, and clarity and good API are much
more important than micro-optimization for something unlikely to be on a
critical path.

That's interesting about the `self._full` variable slowing it down, I think
I'm not surprised (but obviously it depends on just how it's used).  But
one can also simply define RingBuffer.isfull() using
`self.max==len(self.data)` if you prefer that approach.  I doubt
`myringbuffer.isfull()` is something you need to call in an inner loop.

That said, I think my implementation of RingBuffer would probably look more
like (completely untested):

class RingBuffer(object):
    def __init__(self, size_max):
        self.data = [None] * size_max
        self.size_max = size_max
        self.used = 0
        self.cur = 0
    def append(self, val):
        self.data[self.cur] = val
        self.cur = (self.cur+1) % self.size_max
        self.used = max(self.used, self.cur+1)
    def isfull(self):
        self.used == self.size_max

Feel free to try this version against whatever benchmark you have in mind.


On Sun, Oct 18, 2015 at 5:09 PM, Peter Ludemann <pludemann at google.com>
wrote:

> I re-coded the "too clever by half" RingBuffer to use the same design but
> with delegation ... and it ran 50% slower. (Code available on request)
> Then I changed it to switch implementations of append() and get() when it
> got full (the code is below) and it ran at essentially the same speed as
> the original. So, there's no need to be so clever with __class__. Of
> course, this trick of replacing a method is also "too clever by half"; but
> an instance variable for "full" slows it down by 15%.
>
> class RingBuffer(object):
>     def __init__(self, size_max):
>         self.max = size_max
>         self.data = []
>         self.cur = 0
>     def append(self, x):
>         self.data.append(x)
>         if len(self.data) == self.max:
>             self.append = self.append_full
>     def append_full(self, x):
>         self.data[self.cur] = x
>         self.cur = (self.cur + 1) % self.max
>     def get(self):
>         return self.data[self.cur:] + self.data[:self.cur]
>
>
>
> On 18 October 2015 at 08:45, David Mertz <mertz at gnosis.cx> wrote:
>
>> This recipe looks like a bad design to me to start with.  It's
>> too-clever-by-half, IMO.
>>
>> If I were to implement RingBuffer, I wouldn't futz around with the
>> __class__ attribute to change it into another thing when it was full.  A
>> much more obvious API for users would be simply to implement a
>> RingBuffer.isfull() method, perhaps supported by an underlying
>> RingBuffer._full boolean attribute.  That's much friendlier than expecting
>> people to introspect the type of the thing for a question that only
>> occasionally matters; and when it does matter, the question is always
>> conceived exactly as "Is it full?" not "What class is this currently?"
>>
>> So I think I'm still waiting for a compelling example where type(x) !=
>> x.__class__ would be worthwhile (yes, of course it's *possible*)
>>
>> On Sat, Oct 17, 2015 at 10:55 PM, Steven D'Aprano <steve at pearwood.info>
>> wrote:
>>
>>> On Sat, Oct 17, 2015 at 03:45:19PM -0600, Eric Snow wrote:
>>> > In a recent tracker issue about OrderedDict [1] we've had some
>>> > discussion about the use of type(od) as a replacement for
>>> > od.__class__.
>>> [...]
>>> > The more general question of when we use type(obj) vs. obj.__class__
>>> > applies to both the language and to all the stdlib as I expect
>>> > consistency there would result in fewer surprises.  I realize that
>>> > there are some places where using obj.__class__ makes more sense (e.g.
>>> > for some proxy support).  There are other places where using type(obj)
>>> > is the way to go (e.g. special method lookup).  However, the
>>> > difference is muddled enough that usage is inconsistent in the stdlib.
>>> > For example, C-implemented types use Py_TYPE() almost exclusively.
>>> >
>>> > So, would it make sense to establish some concrete guidelines about
>>> > when to use type(obj) vs. obj.__class__?  If so, what would those be?
>>> > It may also be helpful to enumerate use cases for "type(obj) is not
>>> > obj.__class__".
>>>
>>> I for one would like to see a definitive explanation for when they are
>>> different, and when you should use one or the other. The only
>>> obvious example I've seen is the RingBuffer from the Python Cookbook:
>>>
>>> http://code.activestate.com/recipes/68429-ring-buffer/
>>>
>>>
>>>
>>> --
>>> Steve
>>> _______________________________________________
>>> Python-Dev mailing list
>>> Python-Dev at python.org
>>> https://mail.python.org/mailman/listinfo/python-dev
>>> Unsubscribe:
>>> https://mail.python.org/mailman/options/python-dev/mertz%40gnosis.cx
>>>
>>
>>
>>
>> --
>> 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.
>>
>> _______________________________________________
>> Python-Dev mailing list
>> Python-Dev at python.org
>> https://mail.python.org/mailman/listinfo/python-dev
>> Unsubscribe:
>> https://mail.python.org/mailman/options/python-dev/pludemann%40google.com
>>
>>
>


-- 
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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20151018/7f3e5fed/attachment-0001.html>


More information about the Python-Dev mailing list