5

Although variable current_timestamp (and thus also the function return value) and variable time are datetime.datetime class objects, the following code raises AttributeError: type object 'datetime.datetime' has no attribute 'datetime'

from datetime import datetime

def current_time() -> datetime.datetime:
   
    current_timestamp: datetime.datetime = datetime.now()
    return current_timestamp

time: datetime.datetime = current_time()
print(time)

Changing only the annotation for the function return value and the time variable solves the problem.

from datetime import datetime

def current_time() -> datetime:
   
    current_timestamp: datetime.datetime = datetime.now()
    return current_timestamp

time: datetime = current_time()
print(time)

Can anyone explain why the annotation datetime.datetime raises only an AttributeError for the function return value and the time variable, but not for the current_timestamp variable?

I am running Python 3.8.

2 Answers 2

5

From PEP 526, where this syntax is defined

Annotating a local variable will cause the interpreter to treat it as a local, even if it was never assigned to. Annotations for local variables will not be evaluated:

def f():
    x: NonexistentName  # No error.

However, if it is at a module or class level, then the type will be evaluated:

x: NonexistentName  # Error!
class X:
    var: NonexistentName  # Error!

In addition, at the module or class level, if the item being annotated is a simple name, then it and the annotation will be stored in the __annotations__ attribute of that module or class (mangled if private) as an ordered mapping from names to evaluated annotations.

So the reason is that annotations are meant to be a general-purpose tool, leaving open the possibility of using them for things other than type signatures. At the module or class level, you can get access to this information at runtime using reflection techniques, so the value has to exist and will be evaluated. When applied to a local variable, however, no reflection is available on local variables so the annotation is only useful to third-party tools parsing the code, hence Python itself has no reason to evaluate it.

It's worth noting that mypy will fail on the second example, and if you want to use an actual type checker you will have to correct the annotation in every place.

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

1 Comment

Image
Although note, the behavior is changing. If you use from __future__ import annotations then all annotations will not be evaluated, and kept as strings. This becomes the default behavior in Python 3.10, I believe. I'm currently working with a code base that relied on the previous behavior, and it's going to be not fun updating it.
-1

The answer lies in your implementation only.

When you are importing the datetime object from datetime you are expecting the function to return an object of type datetime from the datetime module.

The datetime module has many classes under it and one of that is datetime.

import datetime
def current_time() -> datetime.datetime:
    current_timestamp: datetime = datetime.datetime.now()
    return current_timestamp


time: datetime.datetime = current_time()
print(time)

The above implementation shows if you want to have the function annotation as you have stated in the first case. Hope this helps in understanding the correct import of datetime module.

2 Comments

I'm pretty sure that what he's asking about is specified by Python itself. In any case, I can reliably reproduce the results on my own Python 3.7 as well, so I doubt it's entirely a quirk of the implementation.
ahh..!! got it now, I misinterpreted the question in the first place. Your explanation helped me understand.

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.