1

For example, the PyFloat_Type has many operations in tp_as_number. When this type object gets initialized, all these operations will be written into tp_dict of it with slots.

On the other hand, when I write a custom class with __add__ in it, it's tp_dict will have __add__. This __add__ function will be written into tp_as_number of the type object with slots when the type object gets initialized.

I think tp_dict has recorded all the information we need. Why do we need other members such as tp_as_number? Is this just a historical issue?

1 Answer 1

1

Because when a C level function is using your type, it isn't performing lookup by name in tp_dict (slow), it's directly pulling the pointer from tp_as_number (fast).

A couple pointer dereferences is a trivial cost, requires no reference counting boilerplate, etc.; the cost is measured in single digit cycles most of the time, where a dict lookup is a couple orders of magnitude more expensive or so.

The common case is actually tp_as_number, since a + b will actually use tp_as_number (or tp_as_sequence) knowing that it is synchronized with the equivalent entries in tp_dict; if simple addition required a dict lookup, CPython would be much slower than it is.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much. So, tp_as_number is used because of lookup speed, and tp_dict is used because sometimes we want do things such as super(MyFloat, self).__add__(other). Is that right?
@delphifirst: Yup. Or reference the __add__ for some other purpose, e.g. to generate values that are some other value + 1 efficiently, map((1).__add__, mynums).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.