Skip to content

#262: When () Disappear in Method Calls

Have you ever run into unexpected bugs when checking conditions in your Python code? If so, you might have stumbled upon a subtle, yet common mistake: using a method reference without parentheses when evaluating conditions.

Let us dive into an example to see how this error creeps into our code and why it matters.

1
2
3
4
5
6
7
8
9
import pathlib

file = pathlib.Path(r"C:\Temp\out.png")
if file.is_dir:  # Oops! Missing parentheses
    print(f"{file} is a directory.")
else:
    print(f"{file} is not a directory.")

# prints: C:\Temp\out.png is a directory.

At a quick glance, this code looks perfectly reasonable. It checks if a path is a directory and prints an appropriate message. However, there is a problem: file.is_dir is a method, but the parentheses () are missing. Instead of calling the method, we are referencing the method itself. This will always evaluate to True because functions (when referenced) are truthy objects in Python.

That gives us the output that our file is a directory – then the check is not calling the method but the truthiness of the method reference.

Why it happens

In Python, functions and methods are first-class objects. This means that we can reference a function without invoking it:

1
2
3
4
5
6
def greet():
    return "Hello, World!"

print(greet)  

# prints: <function greet at 0x...>

Referencing greet without parentheses does not execute the function—it merely returns a function object. This behaviour is useful for passing functions as arguments or assigning them to variables, but it becomes a problem when parentheses are accidentally omitted in condition checks.

Without that feature, we could not use Decorators and would lose a lot of the flexibility we saw with Pytest or FastAPI.

Where it gets dangerous

The example above with the wrong output is annoying, but it can get much worse. What if we have a function that should check if a user is an administrator, but we write it again without the ()?

if user.is_admin: # Missing parentheses
    drop_table(table_name)

In this example, user.is_admin is a method. Without the parentheses, it is treated as a function reference, which evaluates as True regardless of the user’s role. The missing () now turned everyone into an administrator and away goes our table.

Avoiding the trap

What can we do at our end to not produce this error?

  1. Call Methods Explicitly: Always use parentheses when calling methods. If you want to call a method like is_admin, write it as user.is_admin().

  2. Use Linting Tools: Tools like Flake8 and Pylint can catch issues where method calls are referenced instead of invoked.

  3. Type Annotations and IDE Warnings: Modern editors like PyCharm and VS Code offer hints and warnings when they detect suspicious code patterns.

Fix the bug

Let us revisit the initial example and write it with the proper method call:

1
2
3
4
5
6
7
8
9
import pathlib

file = pathlib.Path(r"C:\Temp\out.png")
if file.is_dir():  # Correct usage with parentheses
    print(f"{file} is a directory.")
else:
    print(f"{file} is not a directory.")

# prints: C:\Temp\out.png is not a directory.

By adding parentheses to invoke is_dir(), the condition correctly checks whether the specified path is a directory or not.

Conclusion

Omitting parentheses when calling methods in Python is a subtle yet impactful mistake. While it does not trigger syntax errors, it can lead to faulty logic that is hard to debug. By being aware of this common pitfall and using linting tools, you can ensure your condition checks behave as expected.