Why do I only need the name of the module in front of module functions sometimes in python?

Viewed 152

Recently, I've been doing a lot of coding in python and found something interesting. When doing pd.read_csv... I need to have pd in print of the function.

But, when I'm doing something like df.to_csv..., I don't need to put pd in front of the function.

I think it could have to do with pandas itself being a class potentially but I'm not sure.

I've noticed this for other libraries aswell. For example, when using openCV, there are some times when I need to put cv2. before calling a function and other times where I can just put the function without denoting which module it is from.

I've tried researching this but haven't found a very good answer.

1 Answers

You've omitted some things here that make it difficult to see the reason.

First, let's start by using the built-in dir function to see what we actually get when import pandas:

import pandas as pd

print(dir(pd))

#['BooleanDtype', 'Categorical', 'CategoricalDtype', 'CategoricalIndex',
# 'DataFrame', 'DateOffset', 'DatetimeIndex', 'DatetimeTZDtype', 'ExcelFile',
# 'ExcelWriter', 'Flags', 'Float32Dtype', 'Float64Dtype', 'Float64Index',
# 'Grouper', 'HDFStore', 'Index', 'IndexSlice', 'Int16Dtype', 'Int32Dtype',
# 'Int64Dtype', 'Int64Index', 'Int8Dtype', 'Interval', 'IntervalDtype',
# 'IntervalIndex', 'MultiIndex', 'NA', 'NaT', 'NamedAgg', 'Period', 'PeriodDtype',
# 'PeriodIndex', 'RangeIndex', 'Series', 'SparseDtype', 'StringDtype', 'Timedelta',
# 'TimedeltaIndex', 'Timestamp', 'UInt16Dtype', 'UInt32Dtype', 'UInt64Dtype',
# 'UInt64Index', 'UInt8Dtype', '__builtins__', '__cached__', '__doc__',
# '__docformat__', '__file__', '__getattr__', '__git_version__', '__loader__',
# '__name__', '__package__', '__path__', '__spec__', '__version__', '_config',
# '_hashtable', '_is_numpy_dev', '_lib', '_libs', '_np_version_under1p17',
# '_np_version_under1p18', '_testing', '_tslib', '_typing', '_version', 'api',
# 'array', 'arrays', 'bdate_range', 'compat', 'concat', 'core', 'crosstab', 'cut',
# 'date_range', 'describe_option', 'errors', 'eval', 'factorize', 'get_dummies',
# 'get_option', 'infer_freq', 'interval_range', 'io', 'isna', 'isnull',
# 'json_normalize', 'lreshape', 'melt', 'merge', 'merge_asof', 'merge_ordered',
# 'notna', 'notnull', 'offsets', 'option_context', 'options', 'pandas',
# 'period_range', 'pivot', 'pivot_table', 'plotting', 'qcut', 'read_clipboard',
# 'read_csv', 'read_excel', 'read_feather', 'read_fwf', 'read_gbq', 'read_hdf',
# 'read_html', 'read_json', 'read_orc', 'read_parquet', 'read_pickle', 'read_sas',
# 'read_spss', 'read_sql', 'read_sql_query', 'read_sql_table', 'read_stata',
# 'read_table', 'reset_option', 'set_eng_float_format', 'set_option',
# 'show_versions', 'test', 'testing', 'timedelta_range', 'to_datetime',
# 'to_numeric', 'to_pickle', 'to_timedelta', 'tseries', 'unique', 'util', 
# 'value_counts', 'wide_to_long']

I want to point out two specific things that were imported DataFrame and read_csv, which you mentioned in your question.

Now, if you try to use either of these things directly (i.e without the pd prefix), the compiler will throw a NameError:

df = DataFrame(data=[1,2,3]) # NameError: name 'DataFrame' is not defined
x  = read_csv("my_file.csv") # NameError: name 'read_csv'  is not defined

When you directly do import pandas as pd, it keeps all the newly imported pandas functions and objects in a sort of box, which you have to refer to explicitly or else the compiler will not know what function you're referring to. This is done intentionally, since there may be namespace clashes between two modules. Take the where function for example - both numpy and pandas have a where function - if you just call where(), how will the compiler know which one you're referring to?

This can be avoided by using the * operator, like so:

import numpy as np
from pandas import *

x = np.array([1,2,3,4,5])
y = np.where((x%2==0), x, -1)  # you MUST use np prefix here to refer to the numpy 
                               # "box" of imported functions.
z = x.where(x > 1, -2)         # no prefix needed, since * operator was used

Note that you can only use * on one module that you import - you must use a regular import for the rest.

The last thing I want to address is the fact that no prefix is needed when you run df.to_csv(...). This is because df is not something you imported from pandas directly - instead, you initialized df beforehand like this:

import pandas as pd

df = pd.DataFrame(data=[1,2,3])
df.to_csv(...)

No prefix is needed for your function call here because df is something you created! It is a variable whose value has been initialized to a pd.DataFrame object, which can access its internal class method to_csv when you call it. It's almost like an alias for the DataFrame object.

To illustrate this point, you could even do something silly like this:

import pandas as pd

alias_func = pd.read_csv
z = alias_func("my_file.csv")

In short, you only need to use the module prefix when you're actually calling a function imported from a module that was imported without the * operator. You do not need to put the prefix when using variables/objects you initialize to pandas functions or objects, because you have already told the compiler which module's "box" you're referring to, and your new variable just acts as an alias.

Related