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(),isand== - 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
importstatement - 2.10. Assigning custom attributes to a name
- 2.11. The
importlibmodule - 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
intand 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)
| 4When 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
1is 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
objecttype.
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 returnstypewhich 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
inspectmodule
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
intor a custom made class. - All classes whether custom or inbuilt, ultimately inherits from the
objectclass.
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
jwith a value2, which creates an instance of theint` class. - Confirm this using
type()orinstance.__class__. - Inspect the base class of
jusingj.__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,
jis an instance of classint, and it inherits from classobject. - Probing for the base class of
objectwon't print anything sinceobjectis 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]: TrueNOTE: 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]: 32The 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
delare 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
acannot 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 = 10is 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.
ain 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]: 400Explanation:
-
An object of type
intis created in memory and assigned a value of300. -
A name
ais created in the current namespace and points to the address of the object. -
Hence, when
ais called from the prompt, the interpreter fetches the content from memory, ie..300. -
When
ais assigned400, a new object is created in memory with a value of400. -
The name
ain the namespace is now set to point to the new object400. -
Since the object with value
300is 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 definedExplanation:
- Created an object of value
400and assigned it a namea. - Created another namespace variable
band assigned it to bea. This makesbrefer the same addressarefers to. - Since both
aandbrefers to the same address and hence the same object, bothid(a)andid(b)are same. - Hence,
a == banda is bare same as well. del bdeletes the name from the namespace, and does not touch the object in memory.- Since
astill refers to the object, it can be accessed by callinga. - When
del ais 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 == bevaluates the value of the objects thataandbrefers to.a is bevaluates the address of the objects thataandbrefers to.
ie..
a == bcheck if bothaandbhas the same value whilea is bchecks if bothaandbrefers 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
getrefcountfrom the modulesyshelps 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]: 1Multiple 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 callableFortunately, 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]: 1As 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 scopeNOTE:
- In the code snippet above, the function is the inner scope since that is the code being executed.
xfalls 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
globalkeyword ona, the inner scope oftest()was able to manipulate the name. Hence, settinga = "Hi"within the local scope oftest()changes the value ofain the outer scope. This can be proven by callingaoutside the scope oftest().
- Example 5: (Accessing an outer scope using
nonlocalkeyword)
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]: 500Even 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
importkeyword 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
importstatement helps to bring in a module into the current namespace. - The
importstatement 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]: Falsehttps://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]: functionThe 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