Red
Red

Reputation: 51

Python time.time accept self args

According to the following snippet:

import time

def custom_time():
    return time.time()

class TimeWrapper:
    builtin_time = time.time
    def print_builtin(self):
        print(self.builtin_time())

    custom_time = custom_time
    def print_custom(self):
        print(self.custom_time())

wrapper = TimeWrapper()

wrapper.print_builtin()
# 1660163626.7973292
wrapper.print_custom()
# TypeError: custom_time() takes 0 positional arguments but 1 was given
time.time(wrapper)
# TypeError: time.time() takes no arguments (1 given)
custom_time(wrapper)
# TypeError: custom_time() takes 0 positional arguments but 1 was given

I do not understand why wrapper.print_builtin() is working. Is it not supposed to be the equivalent of time.time(wrapper)?

Is there a connection with the unused argument from C implementation ? If it is not the case, I'm still interested in this unused variable.

Upvotes: 2

Views: 78

Answers (2)

Mad Physicist
Mad Physicist

Reputation: 114518

This is an interesting side-effect of how methods really work. A method is just a function in a class dictionary. For example:

class A:
   def b(self):
       print("b")

def c(self):
    print("c")

A.c = c
a = A()

The reason that both b and c are equally methods of A is that they are descriptors. When you access a descriptor from an instance of a class using dot notation, as in a.b or a.c, python will perform the following binding: type(a).b.__get__(a). The bound method is a callable that automatically passes self to the underlying function. Whether you define a method in the class or outside a class and add it to the class dictionary by other means, it will behave the same.

The behavior of c in the example above is exactly the same as custom_time in your example. By setting custom_time = custom_time in the class body, you are creating a method just as if you had placed def custom_time(): in the class body. When you call the subsequently bound method as self.custom_time(), the bound method passes instance self as the first argument, even though the function accepts no arguments. Hence your error.

Upvotes: 2

chepner
chepner

Reputation: 532208

time.time has type builtin_function_or_method, not function. Such objects do not implement the descriptor protocol:

>>> time.time.__get__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'builtin_function_or_method' object has no attribute '__get__'

so self.builtin_time simply returns time.time, not a method object that implicitly passes self as the first argument to time.time.

That is, self.builtin_time() is exactly the same as time.time(), not time.time(self).

Upvotes: 2

Related Questions