4

Cufflinks provides an interface between Panda's DataFrame and plotly iplot in the form of DataFrame.iplot. The first two output examples below do not surprise me, but the third does. Each is run in a new instance of iPython3.

import pandas as pd
pd.DataFrame.iplot

Out[1]: AttributeError: type object 'DataFrame' has no attribute 'iplot'  

import pandas as pd
import cufflinks as cf
pd.DataFrame.iplot

Out[1]: <function cufflinks.plotlytools._iplot>

import cufflinks as cf
import pandas as pd
pd.DataFrame.iplot

Out[1]: <function cufflinks.plotlytools._iplot>

In the first we see pd.DataFrame does not contain iplot by default. When cufflinks is imported after Pandas in the second example, it could easily add an extra method to the DataFrame class to provide additional functionality. However, in the third method the DataFrame class will be defined during the import pandas statement after cufflinks has been imported. How is the additional method still added? This is definitely convenient, as my understanding of how the middle example could work would require always importing the libraries in order, which is undesirable, so I would be interested to know the trick used here.

AJ Pryor
  • 53
  • 1
  • 6
  • Cufflinks loads pandas inside own source code; https://github.com/santosjorge/cufflinks/blob/master/cufflinks/pandastools.py – Serenity Sep 11 '17 at 22:19
  • This occurred to me as well, but even if I choose a different alias for pandas, i.e. `import pandas as pds` then I still find that `pds.DataFrame.iplot` exists as long as cufflinks was imported along the way. – AJ Pryor Sep 12 '17 at 18:46

1 Answers1

1

I'm fairly sure this is because python caches the modules it loads. For example, all of the below expressions are true:

import sys.modules
import pandas as pd
import pandas as pds
print(pd is pds)
print(pd is sys.modules['pandas'])
print(pds is sys.modules['pandas'])

prints:

True
True
True
Jacques Kvam
  • 2,856
  • 1
  • 26
  • 31
  • Nice, I did not know that! Based on your comment I tested the following: import a class A from module M, add a method f to class A, import module M as M2, and finally invoke M2.A().f(), which works correctly. This is very interesting behavior. It is convenient, but I could also imagine how it could cause some nasty bugs. In either case, I don't think this pattern occurs very often, but it is nice to know how it works. – AJ Pryor Oct 25 '18 at 12:34