I have used both. I think the most important consideration is about how sustainable and flexible you want your code to be. For quick checks and "imperative programming" (like Jupyter Notebooks), you could use the minimal shorthand:
train.Datetime.head()
However pretty soon you will realize that when you want to pass variables around that may come from a UI or some other source or debug code efficiently, full notation like this:
train['Datetime'].head()
has main benefits, and it is good to make it a habit early on when programming.
First, in Integrated Development Environments (IDE's) used for editing code, the string 'Datetime' will be highlighted to remind you that it is a "hard dependency" in your code. Whereas the Datetime (no quotes, just a .) will not show the highlighting.
This may not sound like a big deal, but when you are looking a 100's of lines of code (or more), seeing where you have "hardcoded" a variable name is important.
The other main advantage of [] notation is that you can pass in string variables to the notation.
import pandas as pd
import numpy as np
# make some data
n=100
df = pd.DataFrame({
'Fruit': np.random.choice(['Apple', 'Orange', 'Grape'], n),
'Animal': np.random.choice(['Cat', 'Dog', 'Fish'], n),
'x1': np.random.randn(n)})
# some name from a user interface. It could be "Fruit" or "Animal"
group = "Animal"
# use that string variable in an expression (in this case, as a group by)
df.groupby(group).agg(['count', 'mean', 'std'])
Here, even in Stack overflow, you can see that in the df.groupby() that there are no hardcoded strings (in red text). This sepration of user inputs and code that does something is subtle, but extremely important.
Good luck!