Table of Contents generated with DocToc
- Python - Back to basics
- 1. Python and Objects
- 2. Names and Namespaces
- 2.1. Names and Namespaces
- 2.2.
id()
,is
and==
- 2.3. Object Attributes and NameSpaces
- 2.4. Object Reference count
- 2.5. Various methods to set names in a namespace
- 2.6. Overwriting builtin names
- 2.7. Function locals, Scopes, and Name lookups
- 2.8. The Built-in namespace,
locals()
, andglobals()
- 2.9. The
import
statement - 2.10. Assigning custom attributes to a name
- 2.11. The
importlib
module - 2.12. Functions and Namespaces
All initializations (variables, functions, classes, and other instances) done in Python are simply names in the current namespace, that points to an object (a blob with some metadata) in memory.
For example, if a new variable is created, it just points to an object in memory.
Assigning a variable v = 1
at the python REPL (Read Eval Print Loop) prompt triggers the following:
- The REPL reads the assignment statement, and finds the appropriate in-built data type that can represent the data input. ie.. int(), float(), a function, class() etc.
- An instance of the appropriate type class is spawned in memory, which has a specific ID, and is assigned the value. In this example, the REPL finds the type to be
int
and hence a new instance of the classint()
is instantiated. - The instance inherits the attributes of the type class.
- A pointer is created in the current namespace with the name
v
, that points to the instance in memory.
Thus, when creating a variable v = 1
, v
is a reference to the object in memory created by inheriting from the builtin int
type.
Every object has:
- A single type (ie.. every object is an instance of an inbuilt type (class) like int, float etc.. (which is a class)
- A single value
- Attributes, mostly inherited from the builtin type
- One or more base classes (The object is an instance of a builtin class, hence it inherits from it as well)
- A single unique ID (Since an object is an instance of a class, it is a running copy in memory and has an id)
- One or more names, in one or more namespaces (The object created in memory has a reference to it in the namespace)
Object attributes are inherited from the class from which it was instantiated, through classes in the MRO chain, as well as its parent classes.
To list the methods available for an object, use the dir()
function on the object.
In [36]: dir(a)
Out[36]:
['__abs__',
'__add__',
'__and__',
'__bool__',
'__ceil__',
..
....
<omitted>
The type()
builtin function, finds and returns the type of an object.
For example,
In [14]: type(1)
Out[14]: int
In [15]: type(int)
Out[15]: type
In [16]: help(int)
Help on class int in module builtins:
class int(object)
| int(x=0) -> integer
| int(x, base=10) -> integer
|
| Convert a number or string to an integer, or return 0 if no arguments
| are given. If x is a number, return x.__int__(). For floating point
| numbers, this truncates towards zero.
|
| If x is not a number or if base is given, then x must be a string,
| bytes, or bytearray instance representing an integer literal in the
| given base. The literal can be preceded by '+' or '-' and be surrounded
| by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
| Base 0 means to interpret the base from the string as an integer literal.
| >>> int('0b100', base=0)
| 4
When the python interpreter calls the type()
function on a variable or a builtin, it does the following:
- The
type()
function follows the variable name or builtin name to the actual object in memory. - It reads the object metadata and calls the magic method
__class__
on it. - This prints the type of the class from which the object was created, which is of course, the class of the object as well.
In the example above, the integer
1
is an instance of the inbuilt typeint
.
IMPORTANT
- Every object that is created by Python is an instance of an inbuilt type.
- Every type inherits from another type which ultimately ends by inheriting from the
object
type.
NOTE:
- Calling
type(object_name)
internally callsobject_name.__class__
.
In [1]: type(1)
Out[1]: int
In [2]: (1).__class__
Out[2]: int
- If you call
type()
on an inbuilt such asint
, it returnstype
which means it's a base type.
Method Resolution Order
is the order in which a method is resolved.
When a method is called on an object, it first looks up in the inherited methods (from the class from which the object was instantiated), and if not found, moves to its parent class.
Hence, an integer object will first look for the methods under the int()
class, and then the parent class of int()
, ie.. object().
Code example:
In [18]: a = 1
In [19]: a
Out[19]: 1
In [20]: a.__mro__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-20-bc8e99ec9963> in <module>()
----> 1 a.__mro__
AttributeError: 'int' object has no attribute '__mro__'
In [21]: int.__mro__
Out[21]: (int, object)
-
a.__mro__
will fail, since the__mro__
method is not available on objects or its parent class. -
The actual way to get the Method Resolution Order, is to use the
inspect
module
In [33]: import inspect
In [34]: inspect.getmro(int)
Out[34]: (int, object)
In [35]: inspect.getmro(type(a))
Out[35]: (int, object)
Observations:
- Every object has one or more base classes.
- Every object created is an instance of a class which is either inbuilt like
int
or a custom made class. - All classes whether custom or inbuilt, ultimately inherits from the
object
class.
The __class__
method is implemented for almost all the type classes which inherits from the object
class.
This allows to probe the type
and other internals such as the MRO.
- Example 1:
In [98]: True.__mro__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-99-89beb515a8b6> in <module>()
----> 1 True.__mro__
In [99]: type(True)
Out[99]: bool
In [100]: True.__class__
Out[100]: bool
In [101]: True.__class__.__bases__
Out[101]: (int,)
In [102]: True.__class__.__bases__[0]
Out[102]: int
In [103]: True.__class__.__bases__[0].__bases__
Out[103]: (object,)
To understand the inheritance, we try checking the type or the inbuilt True
condition. We find that True.__mro__
does not exist. This is because it's an instance of another class.
To find it, we can use either type()
or True.__class__
. This will print the class that it inherits from. Here, it's the class bool
.
If we use True.__class__.__bases__
, the python interpreter will show the base class of the class the instance is inheriting from, which is int
here. Hence True
is an instance of bool
, and bool
inherits from int
.
True.__class__.bases__[0].__bases__
should print the base class of int
, ie.. the object
class.
- Example 2:
In [128]: j = 2
In [129]: type(j)
Out[129]: int
In [130]: j.__class__
Out[130]: int
In [131]: j.__class__.__base <TAB>
j.__class__.__base__ j.__class__.__bases__
In [131]: j.__class__.__base__
Out[131]: object
In [132]: j.__class__.__bases__
Out[132]: (object,)
- Define a variable
j
with a value2, which creates an instance of the
int` class. - Confirm this using
type()
orinstance.__class__
. - Inspect the base class of
j
usingj.__class__.__base__
orj.__class__.__bases__
j.__class__.__base__
will show a single parent class, while j.__class__.__bases__
shows if there are multiple parent classes.
NOTE:
- Hence,
j
is an instance of classint
, and it inherits from classobject
. - Probing for the base class of
object
won't print anything sinceobject
is the ultimate base class.
The same information can be pulled using the getmro()
method in the inspect
module.
In [37]: type(bool)
Out[37]: type
In [38]: inspect.getmro(bool)
Out[38]: (bool, int, object)
NOTE: For more on MRO, please go through the blog article Method Resolution Order - Object Oriented Programming
Instance objects are not callable. Only functions, classes, or methods are callable.
This means, the function/method/class or any object can be executed and returns a value (can be False
as well)
In [160]: x = int(1212.3)
In [161]: y = "Hello"
In [162]: callable(x)
Out[162]: False
In [163]: callable(y)
Out[163]: False
In [164]: class MyClass(object):
.....: pass
.....:
In [165]: callable(MyClass)
Out[165]: True
In [166]: def myfunc():
.....: pass
.....:
In [167]: callable(myfunc)
Out[167]: True
NOTE: Read more on Callables in the blog article Callables in Python
Object size in memory can be parsed using the getsizeof
method from the sys
module
In [174]: import sys
In [175]: sys.getsizeof("Hello")
Out[175]: 54
In [176]: sys.getsizeof(2**30 + 1)
Out[176]: 32
The help on sys
shows:
In [42]: help(sys.getsizeof)
Help on built-in function getsizeof in module sys:
getsizeof(...)
getsizeof(object, default) -> int
Return the size of object in bytes.
-
A Name is a mapping to a value, ie.. a reference to objects in memory.
-
A Namespace is similar to a dictionary, ie.. it is a set of valid identifier names to object references.
-
Operations such as assignment (=), renaming, and
del
are all namespace operations. -
A scope is a section where a namespace is directly accessible, for example,
dir()
shows the current namespace scope. -
Dot notations ('.') are used to access in-direct namespaces. Some examples:
>>> sys.version_info.major
p.x
>>> "Hello".__add__(" World!")
- It's better not to overwrite builtin names with custom ones, unless there is a strong reason to do so.
NOTE:
A Namespace cannot carry more than one similar name. As an example, multiple variables named
a
cannot exist in a namespace.
Read more on Python Namespaces at Python3 Classes documentation
A dir()
function can list the names in the current namespace.
- Example 1
In [46]: dir()
Out[46]:
['In',
'Out',
'_',
'_1',
'_11',
'_14',
'_19',
'_2',
'_21',
'_26',
...
....
'_iii',
'_oh',
'_sh',
'a',
'exit',
'get_ipython',
'inspect',
'quit',
'sys']
Some important points on Names:
- A name assignment (Creating a variable), renaming, deleting etc.. are all namespace operations.
- Python uses names as a reference to objects in memory, and not like boxes containing a value.
- Variable names are actually labels which you can add (or remove) to an object.
- Deleting a name just removes the reference in the current namespace to the object in memory.
- When all references (names) are removed from the namespace that refers a specific object, the object is garbage collected.
- It's not names (variables) that have types but objects, since objects are actually instances of specific classes (int, str, float etc..)
- Due to point
**6**
, the same name which was referring to an int can be assigned to a str object
- What happens when
a = 10
is set at a python REPL prompt?
- The python interpreter tries to understand the type of RHS value.
- It creates a new object in memory by instantiating an existing type, such as
int()
,class()
,float()
, etc. - The interpreter then goes ahead to create a name which was set on the LHS part, in the current namespace.
a
in this example. - The name acts as a pointer to the newly created object in memory.
- Example 1
In [7]: a = 300
In [8]: a
Out[8]: 300
In [9]: a = 400
In [10]: a
Out[10]: 400
Explanation:
-
An object of type
int
is created in memory and assigned a value of300
. -
A name
a
is created in the current namespace and points to the address of the object. -
Hence, when
a
is called from the prompt, the interpreter fetches the content from memory, ie..300
. -
When
a
is assigned400
, a new object is created in memory with a value of400
. -
The name
a
in the namespace is now set to point to the new object400
. -
Since the object with value
300
is not referenced anymore, it is garbage collected.
The builtins id()
, as well as is
and ==
are valuable to understand the semantics of names in a namespace.
- Example 1
In [26]: a = 400
In [27]: a
Out[27]: 400
In [28]: b = a
In [29]: b
Out[29]: 400
In [30]: id(a)
Out[30]: 139736842153872
In [31]: id(b)
Out[31]: 139736842153872
In [32]: a == b
Out[32]: True
In [33]: a is b
Out[33]: True
In [41]: del b
In [42]: b
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-42-3b5d5c371295> in <module>()
----> 1 b
NameError: name 'b' is not defined
In [43]: a
Out[43]: 400
In [44]: del a
In [45]: a
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-45-60b725f10c9c> in <module>()
----> 1 a
NameError: name 'a' is not defined
Explanation:
- Created an object of value
400
and assigned it a namea
. - Created another namespace variable
b
and assigned it to bea
. This makesb
refer the same addressa
refers to. - Since both
a
andb
refers to the same address and hence the same object, bothid(a)
andid(b)
are same. - Hence,
a == b
anda is b
are same as well. del b
deletes the name from the namespace, and does not touch the object in memory.- Since
a
still refers to the object, it can be accessed by callinga
. - When
del a
is executed, it removes the existing reference to the object. - Once no more references exist in the namespace to an object in memory, the object is garbage-collected.
IMPORTANT:
a == b
evaluates the value of the objects thata
andb
refers to.a is b
evaluates the address of the objects thata
andb
refers to.
ie..
a == b
check if botha
andb
has the same value whilea is b
checks if botha
andb
refers to the exact same object (same address).
- Can we use the same name in a namespace, for a different object type?
In [51]: a = 10
In [52]: id(a)
Out[52]: 139737075565888
In [53]: a
Out[53]: 10
In [54]: a = "Walking"
In [55]: id(a)
Out[55]: 139736828783896
In [56]: a
Out[56]: 'Walking'
Assigning an existing name to another value/type is possible. It sets the pointer to the new type, which is an entirely different object altogether.
When a new object is assigned to an existing name in the namespace, it changes the reference to the new object, and no longer reference the old object.
NOTE: A single name in the namespace cannot refer to multiple objects in memory, just like a file name cannot refer to multiple file content.
Objects get their attributes from the base type the object is instantiated from.
Object attributes are similar to dictionaries, since the attributes of an object are names to methods within the object namespace.
In [19]: a = "test"
In [20]: a.__class__
Out[20]: str
In [21]: dir(a)
Out[21]:
['__add__',
'__class__',
'__contains__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
...
.....
'startswith',
'strip',
'swapcase',
'title',
'translate',
'upper',
'zfill']
Creating a custom name-space can help in understanding the concept of namespaces better.
In Python v3.3, the types
module include a class named SimpleNamespace
which provides a clean namespace to play with.
from types import SimpleNamespace
- In Python v2, it's equivalent to the following custom class, ie.. create a Class and set the attributes manually.
class SimpleNamespace(object):
pass
- New methods can be assigned in the new namespace without overriding any existing ones.
In [64]: from types import SimpleNamespace
In [65]: ns = SimpleNamespace()
In [66]: ns
Out[66]: namespace()
In [67]: ns.a = "A"
In [68]: ns.b = "B"
In [69]: ns
Out[69]: namespace(a='A', b='B')
In [70]: ns.
ns.a ns.b
In [70]: ns.a
Out[70]: 'A'
In [71]: ns.b
Out[71]: 'B'
In [72]: ns.__dict__
Out[72]: {'a': 'A', 'b': 'B'}
The __dict__
special method is available in both Python v2 and v3, for the object, and it returns a dictionary.
- The class
getrefcount
from the modulesys
helps in understanding the references an object has currently.
In [2]: from sys import getrefcount
In [3]: getrefcount(None)
Out[3]: 12405
In [4]: getrefcount(int)
Out[4]: 113
In [5]: a = 10
In [6]: getrefcount(a)
Out[6]: 113
In [7]: b = a
In [8]: getrefcount(a)
Out[8]: 114
In [9]: getrefcount(b)
Out[9]: 114
- Python pre-creates many integer objects for effeciency reasons (Mostly speed)
In [1]: from sys import getrefcount
In [2]: [(i, getrefcount(i)) for i in range(20)]
Out[2]:
[(0, 2150),
(1, 2097),
(2, 740),
(3, 365),
(4, 366),
(5, 196),
(6, 173),
(7, 119),
(8, 248),
(9, 126),
(10, 114),
(11, 110),
(12, 82),
(13, 55),
(14, 50),
(15, 62),
(16, 150),
(17, 52),
(18, 42),
(19, 45)]
The left side value in the tuple shows the int, while the right side shows the number of references to it.
There are multiple ways to set a name for an object, in a namespace.
In [42]: a = 1
In [43]: b = a
In [44]: c = b = a
In [45]: c
Out[45]: 1
In [46]: b
Out[46]: 1
In [47]: a
Out[47]: 1
Multiple names can be assigned in a single go, if the RHS values correspond the LHS names.
RHS has to be iterable so that the assignment will work properly. The RHS can be a list, a tuple, or a series of data.
- Calling the names one-by-one unpacks the tuple and returns the single value.
- When all the names are called simultaneously, they are returned as a tuple.
In [48]: a, b, c = 10, 20, 30
In [49]: a
Out[49]: 10
In [50]: b
Out[50]: 20
In [51]: c
Out[51]: 30
In [52]: a, b, c
Out[52]: (10, 20, 30)
This feature exists only in Python v3.
The examples below are self-explanatory.
- Example 1
In [54]: a, b, c, *d = "HelloWorld!"
In [55]: a
Out[55]: 'H'
In [56]: b
Out[56]: 'e'
In [57]: c
Out[57]: 'l'
In [58]: d
Out[58]: ['l', 'o', 'W', 'o', 'r', 'l', 'd', '!']
- Example 2:
In [59]: a, *b, c = "HelloWorld"
In [60]: a
Out[60]: 'H'
In [61]: b
Out[61]: ['e', 'l', 'l', 'o', 'W', 'o', 'r', 'l']
In [62]: c
Out[62]: 'd'
In [64]: a, b, c
Out[64]: ('H', ['e', 'l', 'l', 'o', 'W', 'o', 'r', 'l'], 'd')
- Example 3:
In [65]: a, *b, c = "Hi"
In [66]: a
Out[66]: 'H'
In [67]: c
Out[67]: 'i'
In [68]: b
Out[68]: []
In [69]: a, b, c
Out[69]: ('H', [], 'i')
Importing modules is another way to get names into the namespace.
The modules could be either part of the standard library, or custom modules written by the developer.
To know more on how import
works, please refer Section 2.9
It is not a good practice to overwrite builtin names, since programs may start acting weirdly. But nevertheless, it is possible.
For example, len()
calls the dunder method __len__()
on the object (provided the type supports len()
), and returns the length. Imagine overwriting it with a custom value.
In [4]: a = "A"
In [5]: len(a)
Out[5]: 1
In [6]: len = "Hello"
In [7]: len(a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-af8c77e09569> in <module>()
----> 1 len(a)
TypeError: 'str' object is not callable
Fortunately, overwriting len
only means that it hides the builtin with the custom assignment. Deleting the custom assignment will re-instate it to the previous state, by unhiding it. The name will resolve to the builtins since it can't find the name in the local namespace.
Continuing from the previous assignments:
In [8]: del len
In [9]: len(a)
Out[9]: 1
As said earlier, there are different scopes, depending on where the call is made. Inner and Outer scopes.
- Example 1 (Outer scope):
In [16]: x = 1
In [17]: def outer():
...: print("%d is in outer scope" % (x))
...:
In [18]: outer()
1 is in outer scope
NOTE:
- In the code snippet above, the function is the inner scope since that is the code being executed.
x
falls outside the inner scope, and hence is in the outer scope.
Executing the function outer()
can access the name x
, even though x
is outside the local scope of outer()
.
- Example 2 (Local scope):
In [24]: a = "Hello"
In [25]: def local():
...: a = "Hi"
...: print("%s is in the local scope" % (a))
...:
In [26]: local()
Hi is in the local scope
In [27]: a
Out[27]: 'Hello'
Here, a
is called within the local()
function. Due to the name lookup resolution method, the first lookup happens in the local scope and proceeds further out. The first hit returns the value.
Even though a
was defined twice, once outside the local scope and then within the local scope, the lookup always starts in the local scope and hence used the value defined within local()
.
But, calling a
prints Hello
, since our local scope is outside the local scope of the function local()
. The function local()
does not touch the variable outside its scope at all, since the same name was available within its local scope.
- Example 3 (Accessing a name in the local scope, before assignment)
Depending on where the name is and how it was referenced, the output may differ.
In [35]: a = "Hello"
In [36]: def test():
...: print("{}".format(a))
...: a = "Hi"
...:
...:
In [37]: test()
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-37-ea594c21b25d> in <module>()
----> 1 test()
<ipython-input-36-68b59252c182> in test()
1 def test():
----> 2 print("{}".format(a))
3 a = "Hi"
4
UnboundLocalError: local variable 'a' referenced before assignment
In [38]: a
Out[38]: 'Hello'
In the example above, a
was defined both within the local scope and outer scope. But the function call errored out with an UnboundLocalError
since the first lookup happens in the local scope, but it was defined after the reference.
- Example 4: (Enforced access of outer scope, and overcoming
UnboundLocalError
)
We can enforce the reference to happen from the outer scope, using the global()
keyword.
In [69]: a
Out[69]: 'Hello'
In [70]: def test():
...: global a
...: print("Calling `a` from outer-scope, `a` = {}".format(a))
...: a = "Hi"
...: print("Calling `a` after setting it in local scope, `a` = {}".format(a))
...:
In [71]: a
Out[71]: 'Hello'
In [72]: test()
Calling `a` from outer-scope, `a` = Hello
Calling `a` after setting it in local scope, `a` = Hi
In [81]: a
Out[81]: 'Hi'
IMPORTANT:
Due to the use of the
global
keyword ona
, the inner scope oftest()
was able to manipulate the name. Hence, settinga = "Hi"
within the local scope oftest()
changes the value ofa
in the outer scope. This can be proven by callinga
outside the scope oftest()
.
- Example 5: (Accessing an outer scope using
nonlocal
keyword)
This is similar to the global
keyword, and gives access to names in outer scopes.
As per the Python3 documentation on nonlocal
:
<Example yet to be updated>
The locals()
and globals()
built-in methods help to list out the local and global scope respectively.
Ideally, the local and global scope are the same since the local scope contains both local and global names. Hence locals()
and globals()
print out the local scope. But it can differ, depending on the code that is executed.
For example, the code below will print a different local scope altogether
In [38]: def hello():
...: a = 100
...: b = 1024
...: print("Printing Local scope", locals())
...: print("###############################")
...: print("Printing Global scope", globals())
...:
In [39]: hello()
Printing Local scope {'b': 1024, 'a': 100}
###############################
Printing Global scope
{'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, ...
....
...... <Long output omitted for brevity>
In the function hello()
defined above, the local scope is restricted to the variables within the function, and hence locals()
can only print the objects tied to the names a
and b
.
But the global scope is the one outside of the local scope of the function hello()
. Therefore, it prints the entire scope outside the local scope.
NOTE:
The local scope change depending where the code is executed. The local scope of a function is the scope within the function, and the global scope is outside it.
The local and global scope are dictionaries, and the local/global namespace can be accessed through locals()
and globals()
builtins.
In [43]: a = 100
In [44]: locals()['a']
Out[44]: 100
In [45]: locals()['a'] = 500
In [46]: a
Out[46]: 500
Even though the names within a scope can be accessed as such, it's not suggested to do so. Read about fast locals
in Python, to understand why.
The import
statement loads the content of a python module (a python source file at a pre-defined location) in memory.
The methods defined in the module are thus available in the current namespace.
- The
import
keyword is a wrapper around the builtin function__import__
, defined in__builtin__
.
In [6]: dir(__builtin__)
Out[6]:
['ArithmeticError',
'AssertionError',
...
.....
'__doc__',
'__import__',
'__loader__',
..
...
'vars',
'zip']
In [7]: print(__builtin__.__import__.__doc__)
__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module
Import a module. Because this function is meant for use by the Python
interpreter and not for general use, it is better to use
importlib.import_module() to programmatically import a module.
The globals argument is only used to determine the context;
they are not modified. The locals argument is unused. The fromlist
should be a list of names to emulate ``from name import ...'', or an
empty list to emulate ``import name''.
When importing a module from a package, note that __import__('A.B', ...)
returns package A when fromlist is empty, but its submodule B when
fromlist is not empty. The level argument is used to determine whether to
perform absolute or relative imports: 0 is absolute, while a positive number
is the number of parent directories to search relative to the current module.
The import
statement
The steps can be summarized as:
- A
import
statement helps to bring in a module into the current namespace. - The
import
statement looks into a pre-defined set of paths, for the file name to be imported. - This file/module is then loaded in memory, to the specific namespace.
- The methods defined in the module becomes available in the current namespace.
In [12]: import sys
In [13]: sys.path
Out[13]:
['',
'/usr/bin',
'/usr/lib64/python36.zip',
'/usr/lib64/python3.6',
'/usr/lib64/python3.6/lib-dynload',
'/usr/lib64/python3.6/site-packages',
'/usr/lib/python3.6/site-packages',
'/usr/lib/python3.6/site-packages/IPython/extensions',
'/home/vimal/.ipython']
- Upon finding the module in any of the paths, the python interpreter loads it into memory and create an object. It stops at the first occurrence of the module.=
- The interpreter creates a name in the current namespace which points to the object in memory.
- The name in the current namespace can be used to access the object's available methods.
NOTE: The del
builtin can be used to delete the name in the current namespace. As always, once the references are null, the objects in memory would be garbage collected.
In [14]: del sys
In [15]: 'sys' in dir() # Checks the presence of the name in the current namespace
Out[15]: False
https://pymotw.com/3/importlib/
Creating a function creates a name in the current namespace. Listing the current namespace, lists the new name.
In [3]: def func1():
...: pass
...:
In [4]: dir()
Out[4]:
['In',
'Out',
'_',
'_2',
'__',
'___',
'__builtin__',
'__builtins__',
..
....
'exit',
'func1',
....]
The interpreter follows the same steps it does for creating any other objects, such as int()
, float()
etc. Here, the object type is a function.
In [7]: type(func1)
Out[7]: function
The function object has attributes of its own within its scope.
In [6]: dir(func1)
Out[6]:
['__annotations__',
'__call__',
'__class__',
'__closure__',
'__code__',
'__defaults__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__get__',
..
....
It is possible to assign custom attributes to the function's scope as well. Please refer Section 2.7 for more details on how to do this, and Scopes.
NOTE: While creating a function object, the name assigned to it goes to the current namespace, as well as the object's local scope.
In [9]: func1
Out[9]: <function __main__.func1>
In [10]: func1.__name__
Out[10]: 'func1'
As with any other function objects, many of the attributes can be changed. This is applicable to the __name__
attribute as well.
In [9]: func1
Out[9]: <function __main__.func1>
In [10]: func1.__name__
Out[10]: 'func1'
In [11]: func1.__name__ = "func2"
In [12]: func1.__name__
Out[12]: 'func2'
In [13]: func2
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-13-c452357c830a> in <module>()
----> 1 func2
NameError: name 'func2' is not defined