python variable scope and object inheritance -- lookup chain
Python Variable Scope
https://data-flair.training/blogs/python-variable-scope/
變數作用域,指代特定的程式碼區域, 在此與區內變數可以被訪問。
變數總是跟作用域繫結, 在其定義時候。
作用域分為四類:
L: 區域性作用域, 例如函式內定義的變數
E:閉包作用域, 函式A內定義函式B, 函式B能看到的函式A中定義的變數,函式A的區域性作用域對於函式B來說就是閉包。
G:全域性作用域, python解析器啟動,建立的環境,由定義的所有全域性變數組成。
B:內建作用域, python提供的內建變數。
What is Python Variable Scope?
The scope of a variable in python is that part of the code where it is visible. Actually, to refer to it, you don’t need to use any prefixes then.
Types of Python Variable Scope
There are 4 types of Variable Scope in Python, let’s discuss them one by one:
https://python-tutorials.in/python-variables-declare-concatenate-global-local/
變數定址過程,實際上是一個鏈式查詢過程,
對於一個函式內部引用的變數,
先查詢函式的local環境中是否定義
如果沒有找到, 則檢視其所屬閉包中是否存在此變數,
如果沒有找到,則檢視全域性環境中是否存在此變數,
如果沒有找到, 則檢視內建環境中是否存在此變數。
這是一個從底向上的一個查詢過程:
local -》enclosed -》 global -》 built-in
Local & Global Variables
In Python when you want to use the same variable for rest of your program or module you declare it a global variable, while if you want to use the variable in a specific function or method, you use a local variable.
Let’s understand this difference between local and global variable with the below program.
- Variable “f” is global in scope and is assigned value 101 which is printed in output
- Variable f is again declared in function and assumes local scope. It is assigned value “I am learning Python.” which is printed out as an output. This variable is different from the global variable “f” define earlier
- Once the function call is over, the local variable f is destroyed. At line 12, when we again, print the value of “f” is it displays the value of global variable f=101
Python Attribute lookup
https://www.demo2s.com/python/python-attribute-lookup.html#:~:text=To%20look%20up%20an%20attribute%2C%20Python%20does%20the,step%201%20then%20search%20the%20parent%20class%28es%29%20dictionaries
類屬性 和 例項屬性 的定址方法:
從下面的解釋,我們看到例項屬性的定址方法,也是一個鏈式結構, 從例項一直尋找到root物件object:
instance scope -> parent class scope -> parent parent class scope -> ... -> object scope
To look up an attribute, Python does the following for class attributes:
- Search the class Dictionary for an attribute
- If the attribute is not found in step 1 then search the parent class(es) dictionaries
For object attributes, Python first searches the instance dictionary and repeats the above steps, it thus performs these steps:
- Search the object/instance dictionary
- If the attribute was not found in step 1, then search the class Dictionary for an attribute
- If the attribute is not found in step 2, then search the parent class dictionaries
Thus given the following statements, different steps are taken each time:
student = Student('john') # Class attribute dictionary print('Student.__dict__:', Student.__dict__) # Instance / Object dictionary print('student.__dict__:', student.__dict__) student = Student('john') print('Student.count:', Student.count) # class lookup print('student.name:', student.name) # instance lookup print('student.count:', student.count) # lookup finds class attribute
https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide
例項屬性定址圖解。
變數定址鏈 vs 屬性定址鏈
對於上面種物件的定址方式學習,我們發現,其都是具有鏈式結構。
但是對於這鏈中每個節點都有哪些內容,python提供的工具給我們來審查。
鏈審查工具 - vars dir
https://www.geeksforgeeks.org/difference-between-dir-and-vars-in-python/
vars(node) 只返回當前節點屬性
dir(node) 不僅僅返回當前節點屬性,還返回node節點的所有父親節點的屬性。
dir() Function:
This function displays more attributes than vars() function, as it is not limited to an instance. It displays the class attributes as well. It also displays the attributes of its ancestor classes.
vars() Function:
This function displays the attribute of an instance in the form of a dictionary.
vars() dir() Returns a dictionary of objects of single class where used Returns a dictionary of objects of single class where used and its base classes It returns a dictionary corresponding to the current local symbol table when no argument is passed It returns the list of names in the current local scope when passed no argument It returns a dictionary corresponding to the object’s symbol table if a module, class or class instance object as argument (or anything else that has a __dict__ attribute) is passed. It attempt to return a list of valid attributes for that object when passed an argument As instances builtin types do not have __dict__ attribute, it returns an Error when used in a built-in type instance. It can be used with all built-in types without error
__dict__
其中每個節點的屬性都被儲存在 __dict__ 的魔法屬性中。
https://www.tutorialspoint.com/What-does-built-in-class-attribute-dict-do-in-Python
類的__dict__, 包括 __init__ 以及類的內建屬性。
例項的 __dict__, 僅僅包括 __init__ 中定義的 屬於與self物件的屬性。
class MyClass(object): class_var = 1 def __init__(self, i_var): self.i_var = i_var foo = MyClass(2) bar = MyClass(3) print MyClass.__dict__ print foo.__dict__ print bar.__dict__
{'__module__': '__main__', 'class_var': 1, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None, '__init__': <function __init__ at 0x0000000004E55CF8>} {'i_var': 2} {'i_var': 3}
object.
__dict__
官方的定義中顯示, __dict__ 就是用於存貯物件的屬性的。
https://docs.python.org/3/library/stdtypes.html#object.__dict__
object.
__dict__
¶A dictionary or other mapping object used to store an object’s (writable) attributes.
https://arrayjson.com/__dict__-python/
module
模組也是一種特殊性的物件, 所有模組內定義的變數, 都被儲存到 它的 __dict__ 屬性中。
https://arrayjson.com/__dict__-python/
Module attributes are implicitly referred using the dictionary __dict__ i.e, whenever an attribute of module is called like module.x = 1 it is internally referenced as module.__dict__[‘x’] = 1
內建審查函式
dir
不帶引數:
返回當前區域性作用域的屬性名。
帶引數有三種使用場景:
(1)引數為模組, 列舉出模組屬性的所有名稱。
(2)引數為型別或者類物件, 列舉出自身屬性名, 和 所有基類的屬性名
(3)其它,這裡應該是類例項, 列舉出自身屬性(例項屬性),父類屬性,以及父類的基類屬性。
https://docs.python.org/3/library/functions.html?highlight=dir#dir
Without arguments, return the list of names in the current local scope. With an argument, attempt to return a list of valid attributes for that object.
If the object has a method named
__dir__()
, this method will be called and must return the list of attributes. This allows objects that implement a custom__getattr__()
or__getattribute__()
function to customize the waydir()
reports their attributes.If the object does not provide
__dir__()
, the function tries its best to gather information from the object’s__dict__
attribute, if defined, and from its type object. The resulting list is not necessarily complete and may be inaccurate when the object has a custom__getattr__()
.The default
dir()
mechanism behaves differently with different types of objects, as it attempts to produce the most relevant, rather than complete, information:
If the object is a module object, the list contains the names of the module’s attributes.
If the object is a type or class object, the list contains the names of its attributes, and recursively of the attributes of its bases.
Otherwise, the list contains the object’s attributes’ names, the names of its class’s attributes, and recursively of the attributes of its class’s base classes.
The resulting list is sorted alphabetically. For example:
import struct
dir() # show the names in the module namespace ['__builtins__', '__name__', 'struct']
dir(struct) # show the names in the struct module ['Struct', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', '_clearcache', 'calcsize', 'error', 'pack', 'pack_into', 'unpack', 'unpack_from']
class Shape:
def __dir__(self):
return ['area', 'perimeter', 'location']
s = Shape()
dir(s) ['area', 'location', 'perimeter']
https://www.geeksforgeeks.org/python-dir-function/
vars
https://docs.python.org/3/library/functions.html?highlight=dir#vars
於dir有兩點不同:
(1)dir返回屬性名稱, vars不僅返回屬性名,還返回屬性值。
(2)vars只返回當前節點屬性,即被儲存在__dict__中的內容, dir不僅包括當前節點, 還遞歸向上遍歷。
Return the
__dict__
attribute for a module, class, instance, or any other object with a__dict__
attribute.Objects such as modules and instances have an updateable
__dict__
attribute; however, other objects may have write restrictions on their__dict__
attributes (for example, classes use atypes.MappingProxyType
to prevent direct dictionary updates).Without an argument,
vars()
acts likelocals()
. Note, the locals dictionary is only useful for reads since updates to the locals dictionary are ignored.A
TypeError
exception is raised if an object is specified but it doesn’t have a__dict__
attribute (for example, if its class defines the__slots__
attribute).
locals
https://docs.python.org/3/library/functions.html?highlight=dir#locals
返回區域性符號表。
Update and return a dictionary representing the current local symbol table. Free variables are returned by
locals()
when it is called in function blocks, but not in class blocks. Note that at the module level,locals()
andglobals()
are the same dictionary.
Free Variable
自由變數,對應 閉包概念。
這裡所謂的自由,其實意味著, 變數沒有在當前程式碼塊定義, 即沒有繫結到當前程式碼塊, 對於當前程式碼塊這個變數是自由的, 不被控制。
https://www.codesansar.com/python-programming/local-global-free-variables-example.htm#:~:text=Free%20Variable%20In%20Python%2C%20there%20exist%20another%20type,there%20then%20it%20is%20known%20as%20free%20variable.
In Python, there exist another type of variable known as Free Variable. If a variable is used in a code block but not defined there then it is known as free variable.
To understand the concept of free variable, lets consider following python program:
Free Variable Example
def outer(): a = 20 def inner(): print('Value of a: %d' %(a)) inner() outer()
Output
Value of a: 20Here variable a is defined in the scope of
outer()
function. If we look atinner()
function, a is not defined there. But, functioninner()
is using variable a and hence variable a ininner()
function is Free Variable.Remember that variable a within
outer()
function is local.
globals
python中作用域(變數可見範圍)是以模組作為最小單位的
所以模組呼叫globals,獲得的是模組中定義的頂級變數, 可以理解為模組的全域性環境。
https://docs.python.org/3/library/functions.html?highlight=dir#globals
Return the dictionary implementing the current module namespace. For code within functions, this is set when the function is defined and remains the same regardless of where the function is called.
module & package
https://docs.python.org/3/tutorial/modules.html#
模組就是一個檔案, 包含python定義和宣告。
A module is a file containing Python definitions and statements. The file name is the module name with the suffix
.py
appended. Within a module, the module’s name (as a string) is available as the value of the global variable__name__
. For instance, use your favorite text editor to create a file calledfibo.py
in the current directory with the following contents:# Fibonacci numbers module def fib(n): # write Fibonacci series up to n a, b = 0, 1 while a < n: print(a, end=' ') a, b = b, a+b print() def fib2(n): # return Fibonacci series up to n result = [] a, b = 0, 1 while a < n: result.append(a) a, b = b, a+b return result
Now enter the Python interpreter and import this module with the following command:
>>> import fibo
This does not enter the names of the functions defined in
fibo
directly in the current symbol table; it only enters the module namefibo
there. Using the module name you can access the functions:>>> fibo.fib(1000) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 >>> fibo.fib2(100) [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] >>> fibo.__name__ 'fibo'
If you intend to use a function often you can assign it to a local name:
>>> fib = fibo.fib >>> fib(500) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
package就是module名稱空間的一種方法。
這是的模組的引用,突破單個檔名的限制, 可以採用層級目錄的方式管理大型模組。
Packages are a way of structuring Python’s module namespace by using “dotted module names”. For example, the module name
A.B
designates a submodule namedB
in a package namedA
. Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or Pillow from having to worry about each other’s module names.Suppose you want to design a collection of modules (a “package”) for the uniform handling of sound files and sound data. There are many different sound file formats (usually recognized by their extension, for example:
.wav
,.aiff
,.au
), so you may need to create and maintain a growing collection of modules for the conversion between the various file formats. There are also many different operations you might want to perform on sound data (such as mixing, adding echo, applying an equalizer function, creating an artificial stereo effect), so in addition you will be writing a never-ending stream of modules to perform these operations. Here’s a possible structure for your package (expressed in terms of a hierarchical filesystem):sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ...When importing the package, Python searches through the directories on
sys.path
looking for the package subdirectory.The
__init__.py
files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such asstring
, unintentionally hiding valid modules that occur later on the module search path. In the simplest case,__init__.py
can just be an empty file, but it can also execute initialization code for the package or set the__all__
variable, described later.Users of the package can import individual modules from the package, for example:
import sound.effects.echo
This loads the submodule
sound.effects.echo
. It must be referenced with its full name.sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)