1. 程式人生 > 其它 >python variable scope and object inheritance -- lookup chain

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.

  1. Variable “f” is global in scope and is assigned value 101 which is printed in output
  2. 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
  3. 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:

  1. Search the class Dictionary for an attribute
  2. 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:

  1. Search the object/instance dictionary
  2. If the attribute was not found in step 1, then search the class Dictionary for an attribute
  3. 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 way dir() 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 a types.MappingProxyType to prevent direct dictionary updates).

Without an argument, vars() acts like locals(). 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() and globals() 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: 20

Here variable a is defined in the scope of outer() function. If we look at inner() function, a is not defined there. But, function inner() is using variable a and hence variable a in inner() 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 called fibo.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 name fibo 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 named B in a package named A. 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 as string, 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)