Một số trình gỡ lỗi cho Python được mô tả bên dưới và chức năng tích hợp sẵn cho phép bạn truy cập vào bất kỳ trình gỡ lỗi nào trong số chúng
Mô-đun pdb là trình gỡ lỗi chế độ bảng điều khiển đơn giản nhưng đầy đủ cho Python. It is part of the standard Python library, and is . You can also write your own debugger by using the code for pdb as an example
The IDLE interactive development environment, which is part of the standard Python distribution [normally available as Tools/scripts/idle3], includes a graphical debugger
PythonWin is a Python IDE that includes a GUI debugger based on pdb. The PythonWin debugger colors breakpoints and has quite a few cool features such as debugging non-PythonWin programs. PythonWin is available as part of pywin32 project and as a part of the ActivePython distribution
Eric is an IDE built on PyQt and the Scintilla editing component
trepan3k is a gdb-like debugger
Visual Studio Code is an IDE with debugging tools that integrates with version-control software
There are a number of commercial Python IDEs that include graphical debuggers. They include
Wing IDE
Komodo IDE
PyCharm
Yes
Pylint and Pyflakes do basic checking that will help you catch bugs sooner
Static type checkers such as Mypy, Pyre, and Pytype can check type hints in Python source code
You don’t need the ability to compile Python to C code if all you want is a stand-alone program that users can download and run without having to install the Python distribution first. Có một số công cụ xác định tập hợp các mô-đun mà chương trình yêu cầu và liên kết các mô-đun này với nhau bằng mã nhị phân Python để tạo ra một tệp thực thi duy nhất
Một là sử dụng công cụ đóng băng, được bao gồm trong cây nguồn Python dưới dạng Tools/freeze. Nó chuyển đổi mã byte Python thành mảng C;
Nó hoạt động bằng cách quét đệ quy nguồn của bạn để tìm các câu lệnh nhập [ở cả hai dạng] và tìm kiếm các mô-đun trong đường dẫn Python chuẩn cũng như trong thư mục nguồn [đối với các mô-đun tích hợp]. Sau đó, nó biến mã byte cho các mô-đun được viết bằng Python thành mã C [bộ khởi tạo mảng có thể được biến thành các đối tượng mã bằng cách sử dụng mô-đun marshal] và tạo một tệp cấu hình tùy chỉnh chỉ chứa các mô-đun dựng sẵn đó thực sự được sử dụng trong . Sau đó, nó biên dịch mã C đã tạo và liên kết nó với phần còn lại của trình thông dịch Python để tạo thành một tệp nhị phân độc lập hoạt động chính xác như tập lệnh của bạn
Các gói sau đây có thể giúp tạo các tệp thực thi giao diện điều khiển và GUI
Nuitka [Đa nền tảng]
PyInstaller [Đa nền tảng]
PyOxidizer [Đa nền tảng]
cx_Freeze [Đa nền tảng]
py2app [chỉ dành cho macOS]
py2exe [chỉ dành cho Windows]
Đúng. The coding style required for standard library modules is documented as PEP 8
It can be a surprise to get the in previously working code when it is modified by adding an assignment statement somewhere in the body of a function
This code
>>> x = 10 >>> def bar[]: .. print[x] ... >>> bar[] 10
works, but this code
>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
results in an
>>> x = 10 >>> def foo[]: .. print[x] .. x += 122
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment
This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124, the compiler recognizes it as a local variable. Consequently when the earlier
>>> x = 10 >>> def foo[]: .. print[x] .. x += 125 attempts to print the uninitialized local variable and an error results
In the example above you can access the outer scope variable by declaring it global
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 10
This explicit declaration is required in order to remind you that [unlike the superficially analogous situation with class and instance variables] you are actually modifying the value of the variable in the outer scope
>>> print[x] 11
You can do a similar thing in a nested scope using the keyword
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 11
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global
Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring for assigned variables provides a bar against unintended side-effects. On the other hand, if
>>> x = 10 >>> def foo[]: .. print[x] .. x += 127 was required for all global references, you’d be using
>>> x = 10 >>> def foo[]: .. print[x] .. x += 127 all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the
>>> x = 10 >>> def foo[]: .. print[x] .. x += 127 declaration for identifying side-effects
Assume you use a for loop to define a few different lambdas [or even plain functions], e. g
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]
This gives you a list that contains 5 lambdas that calculate
>>> x = 10 >>> def foo[]: .. print[x] .. x += 131. You might expect that, when called, they would return, respectively,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 132,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 133,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 134,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 135, and
>>> x = 10 >>> def foo[]: .. print[x] .. x += 136. However, when you actually try you will see that they all return
>>> x = 10 >>> def foo[]: .. print[x] .. x += 136
>>> squares[2][] 16 >>> squares[4][] 16
This happens because
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 is not local to the lambdas, but is defined in the outer scope, and it is accessed when the lambda is called — not when it is defined. At the end of the loop, the value of
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 is
>>> x = 10 >>> def foo[]: .. print[x] .. x += 134, so all the functions now return
>>> x = 10 >>> def foo[]: .. print[x] .. x += 141, i. e.
>>> x = 10 >>> def foo[]: .. print[x] .. x += 136. You can also verify this by changing the value of
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 and see how the results of the lambdas change
>>> x = 8 >>> squares[2][] 64
In order to avoid this, you need to save the values in variables local to the lambdas, so that they don’t rely on the value of the global
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]
Here,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 145 creates a new variable
>>> x = 10 >>> def foo[]: .. print[x] .. x += 146 local to the lambda and computed when the lambda is defined so that it has the same value that
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 had at that point in the loop. This means that the value of
>>> x = 10 >>> def foo[]: .. print[x] .. x += 146 will be
>>> x = 10 >>> def foo[]: .. print[x] .. x += 132 in the first lambda,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 133 in the second,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 151 in the third, and so on. Therefore each lambda will now return the correct result
>>> x = 10 >>> def foo[]: .. print[x] .. x += 10
Note that this behaviour is not peculiar to lambdas, but applies to regular functions too
The canonical way to share information across modules within a single program is to create a special module [often called config or cfg]. Just import the config module in all modules of your application; the module then becomes available as a global name. Because there is only one instance of each module, any changes made to the module object get reflected everywhere. For example
config. py
>>> x = 10 >>> def foo[]: .. print[x] .. x += 11
mod. py
>>> x = 10 >>> def foo[]: .. print[x] .. x += 12
main. py
>>> x = 10 >>> def foo[]: .. print[x] .. x += 13
Note that using a module is also the basis for implementing the singleton design pattern, for the same reason
In general, don’t use
>>> x = 10 >>> def foo[]: .. print[x] .. x += 152. Doing so clutters the importer’s namespace, and makes it much harder for linters to detect undefined names
Import modules at the top of a file. Doing so makes it clear what other modules your code requires and avoids questions of whether the module name is in scope. Using one import per line makes it easy to add and delete module imports, but using multiple imports per line uses less screen space
It’s good practice if you import modules in the following order
standard library modules – e. g. , , ,
third-party library modules [anything installed in Python’s site-packages directory] – e. g.
>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
57,>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
58,>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
59locally developed modules
It is sometimes necessary to move imports to a function or class to avoid problems with circular imports. Gordon McMillan says
Circular imports are fine where both modules use the “import ” form of import. They fail when the 2nd module wants to grab a name out of the first [“from module import name”] and the import is at the top level. That’s because names in the 1st are not yet available, because the first module is busy importing the 2nd.
In this case, if the second module is only used in one function, then the import can easily be moved into that function. By the time the import is called, the first module will have finished initializing, and the second module can do its import
It may also be necessary to move imports out of the top level of code if some of the modules are platform-specific. In that case, it may not even be possible to import all of the modules at the top of the file. In this case, importing the correct modules in the corresponding platform-specific code is a good option
Only move imports into a local scope, such as inside a function definition, if it’s necessary to solve a problem such as avoiding a circular import or are trying to reduce the initialization time of a module. This technique is especially helpful if many of the imports are unnecessary depending on how the program executes. You may also want to move imports into a function if the modules are only ever used in that function. Note that loading a module the first time may be expensive because of the one time initialization of the module, but loading a module multiple times is virtually free, costing only a couple of dictionary lookups. Even if the module name has gone out of scope, the module is probably available in
This type of bug commonly bites neophyte programmers. Hãy xem xét chức năng này
>>> x = 10 >>> def foo[]: .. print[x] .. x += 14
The first time you call this function,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 161 contains a single item. The second time,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 161 contains two items because when
>>> x = 10 >>> def foo[]: .. print[x] .. x += 163 begins executing,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 161 starts out with an item already in it
It is often expected that a function call creates new objects for default values. This is not what happens. Default values are created exactly once, when the function is defined. If that object is changed, like the dictionary in this example, subsequent calls to the function will refer to this changed object
By definition, immutable objects such as numbers, strings, tuples, and
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165, are safe from change. Changes to mutable objects such as dictionaries, lists, and class instances can lead to confusion
Because of this feature, it is good programming practice to not use mutable objects as default values. Instead, use
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165 as the default value and inside the function, check if the parameter is
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165 and create a new list/dictionary/whatever if it is. For example, don’t write
>>> x = 10 >>> def foo[]: .. print[x] .. x += 15
nhưng
>>> x = 10 >>> def foo[]: .. print[x] .. x += 16
Tính năng này có thể hữu ích. When you have a function that’s time-consuming to compute, a common technique is to cache the parameters and the resulting value of each call to the function, and return the cached value if the same value is requested again. This is called “memoizing”, and can be implemented like this
>>> x = 10 >>> def foo[]: .. print[x] .. x += 17
You could use a global variable containing a dictionary instead of the default value; it’s a matter of taste
Collect the arguments using the
>>> x = 10 >>> def foo[]: .. print[x] .. x += 168 and
>>> x = 10 >>> def foo[]: .. print[x] .. x += 169 specifiers in the function’s parameter list; this gives you the positional arguments as a tuple and the keyword arguments as a dictionary. You can then pass these arguments when calling another function by using
>>> x = 10 >>> def foo[]: .. print[x] .. x += 168 and
>>> x = 10 >>> def foo[]: .. print[x] .. x += 169
>>> x = 10 >>> def foo[]: .. print[x] .. x += 18
được xác định bởi các tên xuất hiện trong định nghĩa hàm, trong khi các giá trị thực sự được truyền cho một hàm khi gọi nó. Các tham số xác định những gì một chức năng có thể chấp nhận. Ví dụ, đưa ra định nghĩa hàm
>>> x = 10 >>> def foo[]: .. print[x] .. x += 19
foo, bar và kwargs là các tham số của
>>> x = 10 >>> def foo[]: .. print[x] .. x += 172. Tuy nhiên, khi gọi
>>> x = 10 >>> def foo[]: .. print[x] .. x += 172 chẳng hạn
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment0
các giá trị
>>> x = 10 >>> def foo[]: .. print[x] .. x += 174,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 175 và
>>> x = 10 >>> def foo[]: .. print[x] .. x += 176 là các đối số
Nếu bạn đã viết mã như
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment1
bạn có thể thắc mắc tại sao việc thêm một phần tử vào
>>> x = 10 >>> def foo[]: .. print[x] .. x += 177 lại thay đổi cả
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124
Có hai yếu tố tạo ra kết quả này
Các biến chỉ đơn giản là tên tham chiếu đến các đối tượng. Thực hiện
>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
79 không tạo ra một bản sao của danh sách – nó tạo ra một biến mới>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
77 đề cập đến cùng một đối tượng>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
24 đề cập đến. Điều này có nghĩa là chỉ có một đối tượng [danh sách] và cả>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
24 và>>> x = 10 >>> def foo[]: .. print[x] .. x += 1
77 đều đề cập đến nóDanh sách là , có nghĩa là bạn có thể thay đổi nội dung của chúng
Sau khi gọi đến
>>> x = 10 >>> def foo[]: .. print[x] .. x += 184, nội dung của đối tượng có thể thay đổi đã thay đổi từ
>>> x = 10 >>> def foo[]: .. print[x] .. x += 185 thành
>>> x = 10 >>> def foo[]: .. print[x] .. x += 186. Vì cả hai biến đều tham chiếu đến cùng một đối tượng, sử dụng một trong hai tên sẽ truy cập giá trị đã sửa đổi
>>> x = 10 >>> def foo[]: .. print[x] .. x += 186
Thay vào đó, nếu chúng ta gán một đối tượng bất biến cho
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment2
chúng ta có thể thấy rằng trong trường hợp này,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 và
>>> x = 10 >>> def foo[]: .. print[x] .. x += 177 không còn bằng nhau nữa. This is because integers are , and when we do
>>> x = 10 >>> def foo[]: .. print[x] .. x += 191 we are not mutating the int
>>> x = 10 >>> def foo[]: .. print[x] .. x += 192 by incrementing its value; instead, we are creating a new object [the int
>>> x = 10 >>> def foo[]: .. print[x] .. x += 193] and assigning it to
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 [that is, changing which object
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 refers to]. After this assignment we have two objects [the ints
>>> x = 10 >>> def foo[]: .. print[x] .. x += 193 and
>>> x = 10 >>> def foo[]: .. print[x] .. x += 192] and two variables that refer to them [
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 now refers to
>>> x = 10 >>> def foo[]: .. print[x] .. x += 193 but
>>> x = 10 >>> def foo[]: .. print[x] .. x += 177 still refers to
>>> x = 10 >>> def foo[]: .. print[x] .. x += 192]
Some operations [for example
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment02 and
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment03] mutate the object, whereas superficially similar operations [for example
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment04 and ] create a new object. In general in Python [and in all cases in the standard library] a method that mutates an object will return
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165 to help avoid getting the two types of operations confused. So if you mistakenly write
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment03 thinking it will give you a sorted copy of
>>> x = 10 >>> def foo[]: .. print[x] .. x += 177, you’ll instead end up with
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165, which will likely cause your program to generate an easily diagnosed error
However, there is one class of operations where the same operation sometimes has different behaviors with different types. the augmented assignment operators. For example,
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment10 mutates lists but not tuples or ints [
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment11 is equivalent to
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment12 and mutates
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment13, whereas
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment14 and
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment15 create new objects]
In other words
If we have a mutable object [, , , etc. ], we can use some specific operations to mutate it and all the variables that refer to it will see the change
If we have an immutable object [, , , etc. ], all the variables that refer to it will always see the same value, but operations that transform that value into a new value always return a new object
If you want to know if two variables refer to the same object or not, you can use the operator, or the built-in function
Remember that arguments are passed by assignment in Python. Since assignment just creates references to objects, there’s no alias between an argument name in the caller and callee, and so no call-by-reference per se. Bạn có thể đạt được hiệu quả mong muốn theo một số cách
By returning a tuple of the results
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment
3This is almost always the clearest solution
By using global variables. This isn’t thread-safe, and is not recommended
By passing a mutable [changeable in-place] object
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment
4By passing in a dictionary that gets mutated
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment
5Or bundle up values in a class instance
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment
6There’s almost never a good reason to get this complicated
Your best choice is to return a tuple containing the multiple results
You have two choices. you can use nested scopes or you can use callable objects. For example, suppose you wanted to define
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment24 which returns a function
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment25 that computes the value
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment26. Using nested scopes
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment7
Or using a callable object
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment8
In both cases,
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment9
gives a callable object where
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment27
The callable object approach has the disadvantage that it is a bit slower and results in slightly longer code. However, note that a collection of callables can share their signature via inheritance
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 100
Object can encapsulate state for several methods
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 101
Here
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment28,
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment29 and
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment30 act like functions which share the same counting variable
In general, try or for the general case. Not all objects can be copied, but most can
Some objects can be copied more easily. Dictionaries have a method
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 102
Trình tự có thể được sao chép bằng cách cắt
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 103
Đối với một thể hiện
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 của một lớp do người dùng định nghĩa, trả về một danh sách theo thứ tự bảng chữ cái gồm các tên chứa các thuộc tính và phương thức của thể hiện cũng như các thuộc tính được xác định bởi lớp của nó
Nói chung là không thể, bởi vì các đối tượng không thực sự có tên. Về cơ bản, phép gán luôn gắn tên với giá trị; . Hãy xem xét đoạn mã sau
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 104
Có thể cho rằng lớp có một tên. mặc dù nó bị ràng buộc với hai tên và được gọi thông qua tên
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment38, cá thể đã tạo vẫn được báo cáo là một cá thể của lớp
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment39. Tuy nhiên, không thể nói liệu tên của đối tượng là
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment40 hay
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment41, vì cả hai tên đều bị ràng buộc với cùng một giá trị
Nói chung, mã của bạn không cần thiết phải “biết tên” của các giá trị cụ thể. Trừ khi bạn đang cố tình viết các chương trình hướng nội, đây thường là dấu hiệu cho thấy việc thay đổi cách tiếp cận có thể có lợi
trong comp. lang thang. trăn, Fredrik Lundh đã từng đưa ra một phép loại suy tuyệt vời để trả lời cho câu hỏi này
Giống như cách bạn lấy tên của con mèo mà bạn tìm thấy trên hiên nhà của mình. bản thân con mèo [đối tượng] không thể cho bạn biết tên của nó và nó cũng không thực sự quan tâm – vì vậy cách duy nhất để biết nó được gọi là gì là hỏi tất cả những người hàng xóm [không gian tên] của bạn xem đó có phải là con mèo [đối tượng] của họ không…
…. và đừng ngạc nhiên nếu bạn thấy rằng nó được biết đến với nhiều tên hoặc không có tên nào cả
Dấu phẩy không phải là toán tử trong Python. Hãy xem xét phiên này
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 105
Vì dấu phẩy không phải là toán tử, mà là dấu phân cách giữa các biểu thức, phần trên được đánh giá như thể bạn đã nhập
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 106
không phải
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 107
Điều này cũng đúng với các toán tử gán khác nhau [
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment42,
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment10, v.v.]. Chúng không thực sự là toán tử mà là dấu phân cách cú pháp trong câu lệnh gán
Có, có. Cú pháp như sau
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 108
Trước khi cú pháp này được giới thiệu trong Python 2. 5, một thành ngữ phổ biến là sử dụng các toán tử logic
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 109
Tuy nhiên, thành ngữ này không an toàn vì nó có thể cho kết quả sai khi on_true có giá trị boolean sai. Do đó, tốt hơn hết là sử dụng biểu mẫu
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment44
Đúng. Thông thường, điều này được thực hiện bằng cách lồng vào nhau trong
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment45. Xem ba ví dụ sau, được điều chỉnh một chút từ Ulf Bartelt
>>> print[x] 110
Đừng thử điều này ở nhà, trẻ em
Dấu gạch chéo trong danh sách đối số của hàm biểu thị rằng các tham số trước nó chỉ là vị trí. Tham số chỉ vị trí là những tham số không có tên có thể sử dụng bên ngoài. Khi gọi một hàm chỉ chấp nhận các tham số chỉ vị trí, các đối số được ánh xạ tới các tham số chỉ dựa trên vị trí của chúng. Ví dụ: là một hàm chấp nhận các tham số chỉ vị trí. tài liệu của nó trông như thế này
>>> print[x] 111
Dấu gạch chéo ở cuối danh sách tham số có nghĩa là cả hai tham số đều chỉ có vị trí. Do đó, việc gọi với đối số từ khóa sẽ dẫn đến lỗi
>>> print[x] 112
Để chỉ định một chữ số bát phân, hãy đặt trước giá trị bát phân bằng số 0, sau đó là chữ "o" viết thường hoặc viết hoa. Ví dụ: để đặt biến “a” thành giá trị bát phân “10” [8 ở dạng thập phân], hãy nhập
>>> print[x] 113
Hệ thập lục phân thật dễ dàng. Chỉ cần đặt trước số thập lục phân bằng số 0, sau đó là chữ "x" viết thường hoặc viết hoa. Các chữ số thập lục phân có thể được chỉ định bằng chữ thường hoặc chữ hoa. Ví dụ, trong trình thông dịch Python
>>> print[x] 114
Nó chủ yếu được thúc đẩy bởi mong muốn rằng
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment49 có cùng dấu hiệu với
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment50. Nếu bạn muốn điều đó, và cũng muốn
>>> print[x] 115
sau đó phép chia số nguyên phải trả về sàn. C cũng yêu cầu giữ danh tính đó, và sau đó các trình biên dịch cắt bớt
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment51 cần làm cho
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment49 có cùng dấu như
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment53
Có rất ít trường hợp sử dụng thực tế cho
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment49 khi
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment50 là số âm. Khi
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment50 dương, có rất nhiều và hầu như tất cả chúng đều hữu ích hơn khi
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment49 là
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment58. Nếu bây giờ đồng hồ chỉ 10 giờ thì 200 giờ trước nó chỉ mấy giờ?
Cố gắng tra cứu thuộc tính chữ
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment20 theo cách thông thường sẽ cho kết quả a vì dấu chấm được xem là dấu thập phân
>>> print[x] 116
Giải pháp là tách chữ khỏi dấu chấm bằng dấu cách hoặc dấu ngoặc đơn
>>> print[x] 117
Đối với số nguyên, hãy sử dụng hàm tạo kiểu dựng sẵn, e. g.
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment64. Tương tự, chuyển đổi sang dấu phẩy động, e. g.
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment66
Theo mặc định, những điều này giải thích số dưới dạng số thập phân, do đó,
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment67 đúng và
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment68 tăng.
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment70 lấy cơ sở để chuyển đổi từ làm đối số tùy chọn thứ hai, vì vậy
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment71. Nếu cơ sở được chỉ định là 0, số được diễn giải bằng quy tắc của Python. một '0o' hàng đầu biểu thị bát phân và '0x' biểu thị một số hex
Không sử dụng chức năng tích hợp nếu tất cả những gì bạn cần là chuyển đổi chuỗi thành số. sẽ chậm hơn đáng kể và có nguy cơ bảo mật. ai đó có thể chuyển cho bạn một biểu thức Python có thể có tác dụng phụ không mong muốn. Ví dụ: ai đó có thể vượt qua
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment74 sẽ xóa thư mục chính của bạn
cũng có tác dụng diễn giải các số dưới dạng biểu thức Python, do đó e. g.
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment76 gives a syntax error because Python does not allow leading ‘0’ in a decimal number [except ‘0’]
Để chuyển đổi, e. g. , số
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment77 thành chuỗi
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment78, sử dụng hàm tạo kiểu có sẵn. Nếu bạn muốn biểu diễn hệ thập lục phân hoặc bát phân, hãy sử dụng các hàm tích hợp sẵn hoặc. Để biết định dạng ưa thích, hãy xem phần và, e. g.
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment82 sản lượng
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment83 và
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment84 sản lượng
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment85
Bạn không thể, bởi vì các chuỗi là bất biến. Trong hầu hết các trường hợp, bạn chỉ cần tạo một chuỗi mới từ các phần khác nhau mà bạn muốn lắp ráp nó từ đó. Tuy nhiên, nếu bạn cần một đối tượng có khả năng sửa đổi dữ liệu unicode tại chỗ, hãy thử sử dụng một đối tượng hoặc mô-đun
>>> print[x] 118
Có nhiều kỹ thuật khác nhau
Tốt nhất là sử dụng một từ điển ánh xạ các chuỗi thành các hàm. Ưu điểm chính của kỹ thuật này là các chuỗi không cần khớp với tên của các hàm. Đây cũng là kỹ thuật chính được sử dụng để mô phỏng cấu trúc trường hợp
>>> print[x] 11
9Sử dụng chức năng tích hợp
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 11
0Lưu ý rằng hoạt động trên bất kỳ đối tượng nào, bao gồm các lớp, thể hiện của lớp, mô-đun, v.v.
Điều này được sử dụng ở một số nơi trong thư viện tiêu chuẩn, như thế này
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 11
1Sử dụng để phân giải tên hàm
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 11
2
Bạn có thể sử dụng
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment91 để xóa tất cả các lần xuất hiện của bất kỳ dấu kết thúc dòng nào khỏi cuối chuỗi
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment92 mà không xóa khoảng trắng ở cuối khác. Nếu chuỗi
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment92 đại diện cho nhiều hơn một dòng, với một số dòng trống ở cuối, các dấu kết thúc dòng cho tất cả các dòng trống sẽ bị xóa
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 113
Vì điều này thường chỉ được mong muốn khi đọc văn bản từng dòng một, nên sử dụng
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment94 theo cách này hoạt động tốt
Không phải như vậy
Để phân tích cú pháp đầu vào đơn giản, cách tiếp cận đơn giản nhất thường là chia dòng thành các từ được phân tách bằng khoảng trắng bằng cách sử dụng phương thức của đối tượng chuỗi, sau đó chuyển đổi chuỗi thập phân thành giá trị số bằng cách sử dụng hoặc.
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment95 hỗ trợ tham số “sep” tùy chọn, hữu ích nếu dòng sử dụng thứ gì đó không phải khoảng trắng làm dấu phân cách
Để phân tích cú pháp đầu vào phức tạp hơn, các biểu thức chính quy mạnh hơn
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment99 của C và phù hợp hơn cho tác vụ
xem
Chuỗi thô kết thúc bằng số lẻ dấu gạch chéo ngược sẽ thoát khỏi dấu ngoặc kép của chuỗi
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 114
Có một số cách giải quyết cho việc này. Một là sử dụng các chuỗi thông thường và nhân đôi dấu gạch chéo ngược
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 115
Một cách khác là nối một chuỗi thông thường có dấu gạch chéo ngược đã thoát vào chuỗi thô
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 116
Cũng có thể sử dụng để nối thêm dấu gạch chéo ngược trên Windows
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 117
Lưu ý rằng mặc dù dấu gạch chéo ngược sẽ "thoát" một trích dẫn nhằm mục đích xác định vị trí kết thúc của chuỗi thô, nhưng không có dấu gạch chéo ngược nào xảy ra khi diễn giải giá trị của chuỗi thô. Nghĩa là, dấu gạch chéo ngược vẫn hiện diện trong giá trị của chuỗi thô
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 118
Cũng xem thông số kỹ thuật trong
Đó là một khó khăn, nói chung. Đầu tiên, đây là danh sách những điều cần nhớ trước khi đi sâu hơn
Các đặc điểm hiệu suất khác nhau giữa các triển khai Python. Câu hỏi thường gặp này tập trung vào
Hành vi có thể khác nhau giữa các hệ điều hành, đặc biệt khi nói về I/O hoặc đa luồng
Bạn phải luôn tìm các điểm nóng trong chương trình của mình trước khi cố gắng tối ưu hóa bất kỳ mã nào [xem mô-đun]
Viết tập lệnh điểm chuẩn sẽ cho phép bạn lặp lại nhanh chóng khi tìm kiếm các cải tiến [xem mô-đun]
Bạn nên có phạm vi mã tốt [thông qua thử nghiệm đơn vị hoặc bất kỳ kỹ thuật nào khác] trước khi có khả năng giới thiệu hồi quy ẩn trong các tối ưu hóa tinh vi
Điều đó đang được nói, có rất nhiều thủ thuật để tăng tốc mã Python. Dưới đây là một số nguyên tắc chung cần thiết để đạt được mức hiệu suất chấp nhận được
Làm cho thuật toán của bạn nhanh hơn [hoặc thay đổi thành thuật toán nhanh hơn] có thể mang lại lợi ích lớn hơn nhiều so với việc cố gắng rải các thủ thuật tối ưu hóa vi mô lên toàn bộ mã của bạn
Sử dụng đúng cấu trúc dữ liệu. Nghiên cứu tài liệu cho các và mô-đun
Khi thư viện tiêu chuẩn cung cấp một nguyên hàm để làm một việc gì đó, có khả năng [mặc dù không được đảm bảo] sẽ nhanh hơn bất kỳ giải pháp thay thế nào mà bạn có thể nghĩ ra. Điều này hoàn toàn đúng đối với các nguyên thủy được viết bằng C, chẳng hạn như nội trang và một số loại tiện ích mở rộng. Ví dụ: đảm bảo sử dụng phương thức tích hợp sẵn hoặc chức năng liên quan để thực hiện sắp xếp [và xem ví dụ về cách sử dụng nâng cao vừa phải]
Sự trừu tượng hóa có xu hướng tạo ra sự gián tiếp và buộc trình thông dịch phải làm việc nhiều hơn. Nếu mức độ gián tiếp lớn hơn số lượng công việc hữu ích được thực hiện, chương trình của bạn sẽ chậm hơn. Bạn nên tránh sự trừu tượng quá mức, đặc biệt là dưới dạng các hàm hoặc phương thức nhỏ [cũng thường gây bất lợi cho khả năng đọc]
Nếu bạn đã đạt đến giới hạn mà Python thuần túy có thể cho phép, sẽ có những công cụ giúp bạn tiến xa hơn. Ví dụ: Cython có thể biên dịch phiên bản mã Python được sửa đổi một chút thành phần mở rộng C và có thể được sử dụng trên nhiều nền tảng khác nhau. Cython có thể tận dụng quá trình biên dịch [và các chú thích loại tùy chọn] để làm cho mã của bạn nhanh hơn đáng kể so với khi diễn giải. Nếu bạn tự tin vào kỹ năng lập trình C của mình, bạn cũng có thể tự
Xem thêm
Trang wiki dành cho mẹo hiệu suất
và các đối tượng là bất biến, do đó việc nối nhiều chuỗi lại với nhau là không hiệu quả vì mỗi lần nối tạo ra một đối tượng mới. Trong trường hợp chung, tổng chi phí thời gian chạy là bậc hai trong tổng độ dài chuỗi
Để tích lũy nhiều đối tượng, thành ngữ được đề xuất là đặt chúng vào một danh sách và gọi ở cuối
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 119
[another reasonably efficient idiom is to use ]
Để tích lũy nhiều đối tượng, thành ngữ được khuyến nghị là mở rộng một đối tượng bằng cách sử dụng phép nối tại chỗ [toán tử
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment10]
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]0
Hàm tạo kiểu
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1014 chuyển đổi bất kỳ chuỗi nào [thực tế là bất kỳ có thể lặp lại nào] thành một bộ có cùng các phần tử theo cùng một thứ tự
Ví dụ: _______6_______15 mang lại _______6_______16 và _______6_______17 mang lại _______6_______18. Nếu đối số là một bộ, thì nó không tạo một bản sao mà trả về cùng một đối tượng, vì vậy sẽ rẻ hơn nếu bạn không chắc chắn rằng một đối tượng đã là một bộ
Hàm tạo kiểu
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1020 chuyển đổi bất kỳ chuỗi nào hoặc có thể lặp lại thành một danh sách có cùng mục theo cùng một thứ tự. Ví dụ: _______6_______21 mang lại _______6_______22 và _______6_______23 mang lại _______6_______24. Nếu đối số là một danh sách, nó sẽ tạo một bản sao giống như
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1025
Chuỗi Python được lập chỉ mục với số dương và số âm. Đối với các số dương 0 là chỉ số đầu tiên 1 là chỉ số thứ hai, v.v. Đối với các chỉ số âm -1 là chỉ số cuối cùng và -2 là chỉ số áp chót [kế tiếp đến cuối cùng], v.v. Hãy nghĩ về
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1026 giống như
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1027
Sử dụng chỉ số âm có thể rất thuận tiện. Ví dụ:
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1028 là toàn bộ chuỗi ngoại trừ ký tự cuối cùng của nó, rất hữu ích để xóa dòng mới ở cuối chuỗi
Sử dụng chức năng tích hợp
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]1
Điều này sẽ không chạm vào trình tự ban đầu của bạn, nhưng tạo một bản sao mới với thứ tự đảo ngược để lặp lại
Xem Sách dạy nấu ăn Python để biết một cuộc thảo luận dài về nhiều cách để thực hiện việc này
https. //mã số. trạng thái kích hoạt. com/công thức nấu ăn/52560/
Nếu bạn không ngại sắp xếp lại danh sách, hãy sắp xếp nó rồi quét từ cuối danh sách, xóa các mục trùng lặp khi bạn thực hiện
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]2
Nếu tất cả các phần tử của danh sách có thể được sử dụng làm khóa thiết lập [i. e. họ là tất cả] điều này thường nhanh hơn
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]3
Điều này chuyển đổi danh sách thành một tập hợp, do đó loại bỏ các bản sao và sau đó quay lại danh sách
Như với việc loại bỏ các bản sao, lặp lại rõ ràng ngược lại với điều kiện xóa là một khả năng. Tuy nhiên, sẽ dễ dàng và nhanh hơn khi sử dụng thay thế lát cắt bằng phép lặp chuyển tiếp rõ ràng hoặc ẩn. Đây là ba biến thể
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]4
Việc hiểu danh sách có thể nhanh nhất
Sử dụng một danh sách
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]5
Các danh sách tương đương với các mảng C hoặc Pascal về độ phức tạp về thời gian của chúng;
Mô-đun
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment87 cũng cung cấp các phương thức để tạo các mảng có loại cố định với các biểu diễn nhỏ gọn, nhưng chúng chậm hơn để lập chỉ mục so với danh sách. Cũng lưu ý rằng NumPy và các gói bên thứ ba khác cũng xác định các cấu trúc giống như mảng với các đặc điểm khác nhau
Để có danh sách được liên kết kiểu Lisp, bạn có thể mô phỏng các ô khuyết điểm bằng cách sử dụng các bộ dữ liệu
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]6
Nếu muốn có khả năng thay đổi, bạn có thể sử dụng danh sách thay vì bộ dữ liệu. Ở đây, dạng tương tự của một chiếc ô tô Lisp là
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1031 và dạng tương tự của cdr là
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1032. Chỉ làm điều này nếu bạn chắc chắn rằng mình thực sự cần, vì nó thường chậm hơn rất nhiều so với việc sử dụng danh sách Python
Có thể bạn đã thử tạo một mảng nhiều chiều như thế này
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]7
Điều này có vẻ đúng nếu bạn in nó
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]8
Nhưng khi bạn chỉ định một giá trị, nó sẽ hiển thị ở nhiều nơi
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda: x**2]9
Lý do là việc sao chép một danh sách với
>>> x = 10 >>> def foo[]: .. print[x] .. x += 168 không tạo ra các bản sao, nó chỉ tạo ra các tham chiếu đến các đối tượng hiện có.
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1034 tạo một danh sách chứa 3 tham chiếu đến cùng một danh sách có độ dài hai. Các thay đổi đối với một hàng sẽ hiển thị trong tất cả các hàng, điều này gần như chắc chắn không phải là điều bạn muốn
The suggested approach is to create a list of the desired length first and then fill in each element with a newly created list
>>> squares[2][] 16 >>> squares[4][] 160
Điều này tạo ra một danh sách chứa 3 danh sách khác nhau có độ dài hai. Bạn cũng có thể sử dụng cách hiểu danh sách
>>> squares[2][] 16 >>> squares[4][] 161
Hoặc, bạn có thể sử dụng tiện ích mở rộng cung cấp kiểu dữ liệu ma trận;
Để gọi một phương thức hoặc hàm và tích lũy các giá trị trả về là một danh sách, a là một giải pháp tao nhã
>>> squares[2][] 16 >>> squares[4][] 162
Để chỉ chạy phương thức hoặc hàm mà không lưu các giá trị trả về, một vòng lặp đơn giản là đủ
>>> squares[2][] 16 >>> squares[4][] 163
Điều này là do sự kết hợp của thực tế là các toán tử gán tăng cường là các toán tử gán và sự khác biệt giữa các đối tượng có thể thay đổi và không thể thay đổi trong Python
Cuộc thảo luận này áp dụng chung khi các toán tử gán tăng cường được áp dụng cho các phần tử của bộ trỏ đến các đối tượng có thể thay đổi, nhưng chúng tôi sẽ sử dụng
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment16 và
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment10 làm ví dụ của chúng tôi
Nếu bạn đã viết
>>> squares[2][] 16 >>> squares[4][] 164
Lý do ngoại lệ phải rõ ràng ngay lập tức.
>>> x = 10 >>> def foo[]: .. print[x] .. x += 133 được thêm vào đối tượng
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1039 trỏ đến [
>>> x = 10 >>> def foo[]: .. print[x] .. x += 133], tạo ra đối tượng kết quả,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 151, nhưng khi chúng tôi cố gắng gán kết quả tính toán,
>>> x = 10 >>> def foo[]: .. print[x] .. x += 151, cho phần tử
>>> x = 10 >>> def foo[]: .. print[x] .. x += 132 của bộ dữ liệu, chúng tôi gặp lỗi vì chúng tôi không thể
Dưới vỏ bọc, điều mà câu lệnh gán tăng cường này đang thực hiện là xấp xỉ điều này
>>> squares[2][] 16 >>> squares[4][] 165
Đó là phần gán của hoạt động tạo ra lỗi, vì một bộ dữ liệu là bất biến
Khi bạn viết một cái gì đó như
>>> squares[2][] 16 >>> squares[4][] 166
Ngoại lệ đáng ngạc nhiên hơn một chút và thậm chí còn đáng ngạc nhiên hơn là mặc dù có lỗi nhưng phần bổ sung vẫn hoạt động
>>> squares[2][] 16 >>> squares[4][] 167
Để biết tại sao điều này lại xảy ra, bạn cần biết rằng [a] nếu một đối tượng triển khai một phương thức ma thuật, nó sẽ được gọi khi thực thi phép gán tăng cường
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment10 và giá trị trả về của nó là giá trị được sử dụng trong câu lệnh gán; . Đó là lý do tại sao chúng tôi nói rằng đối với danh sách,
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment10 là “viết tắt” của
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1049
>>> squares[2][] 16 >>> squares[4][] 168
Điều này tương đương với
>>> squares[2][] 16 >>> squares[4][] 169
Đối tượng được trỏ tới bởi a_list đã bị thay đổi và con trỏ tới đối tượng bị thay đổi được gán lại cho
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment13. Kết quả cuối cùng của phép gán là không hoạt động, vì nó là một con trỏ tới cùng một đối tượng mà
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment13 đã trỏ tới trước đó, nhưng phép gán vẫn diễn ra
Do đó, trong ví dụ bộ dữ liệu của chúng tôi, những gì đang xảy ra tương đương với
>>> x = 8 >>> squares[2][] 640
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1044 thành công và do đó danh sách được mở rộng, nhưng mặc dù
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1053 trỏ đến cùng một đối tượng mà
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1039 đã trỏ đến, phép gán cuối cùng đó vẫn dẫn đến lỗi, bởi vì các bộ dữ liệu là bất biến
Kỹ thuật này, do Randal Schwartz của cộng đồng Perl, sắp xếp các phần tử của danh sách theo một số liệu ánh xạ từng phần tử tới “giá trị sắp xếp” của nó. Trong Python, sử dụng đối số
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1055 cho phương thức
>>> x = 8 >>> squares[2][] 641
Hợp nhất chúng thành một bộ lặp gồm các bộ, sắp xếp danh sách kết quả, sau đó chọn ra phần tử bạn muốn
>>> x = 8 >>> squares[2][] 642
Một lớp là loại đối tượng cụ thể được tạo bằng cách thực hiện một câu lệnh lớp. Các đối tượng lớp được sử dụng làm mẫu để tạo các đối tượng thể hiện, thể hiện cả dữ liệu [thuộc tính] và mã [phương thức] cụ thể cho một kiểu dữ liệu
Một lớp có thể dựa trên một hoặc nhiều lớp khác, được gọi là [các] lớp cơ sở của nó. Sau đó, nó kế thừa các thuộc tính và phương thức của các lớp cơ sở của nó. Điều này cho phép một mô hình đối tượng được tinh chỉnh liên tục bằng kế thừa. Bạn có thể có một lớp
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1057 chung cung cấp các phương thức truy cập cơ bản cho hộp thư và các lớp con như
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1058,
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1059,
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1060 xử lý các định dạng hộp thư cụ thể khác nhau
Một phương thức là một chức năng trên một số đối tượng
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 mà bạn thường gọi là
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1062. Các phương thức được định nghĩa là các hàm bên trong định nghĩa lớp
>>> x = 8 >>> squares[2][] 643
Bản thân chỉ là một tên quy ước cho đối số đầu tiên của một phương thức. Một phương thức được định nghĩa là
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1063 nên được gọi là
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1064 đối với một số trường hợp
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 của lớp mà định nghĩa xảy ra;
Xem thêm
Sử dụng chức năng tích hợp. Bạn có thể kiểm tra xem một đối tượng có phải là một thể hiện của bất kỳ lớp nào hay không bằng cách cung cấp một bộ thay vì một lớp đơn lẻ, e. g.
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1068 và cũng có thể kiểm tra xem một đối tượng có phải là một trong các loại dựng sẵn của Python hay không, e. g.
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1069 hoặc
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1070
Lưu ý rằng cũng kiểm tra kế thừa ảo từ một. Vì vậy, bài kiểm tra sẽ trả về ___6_______72 cho một lớp đã đăng ký ngay cả khi không được kế thừa trực tiếp hoặc gián tiếp từ nó. Để kiểm tra "sự kế thừa thực sự", hãy quét lớp
>>> x = 8 >>> squares[2][] 644
>>> x = 8 >>> squares[2][] 645
Lưu ý rằng hầu hết các chương trình không thường xuyên sử dụng trên các lớp do người dùng định nghĩa. Nếu bạn đang tự phát triển các lớp, thì một kiểu hướng đối tượng phù hợp hơn là định nghĩa các phương thức trên các lớp đóng gói một hành vi cụ thể, thay vì kiểm tra lớp của đối tượng và thực hiện một việc khác dựa trên lớp đó là gì. Ví dụ: nếu bạn có một chức năng thực hiện điều gì đó
>>> x = 8 >>> squares[2][] 646
Một cách tiếp cận tốt hơn là định nghĩa một phương thức
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1074 trên tất cả các lớp và chỉ cần gọi nó là
>>> x = 8 >>> squares[2][] 647
Ủy quyền là một kỹ thuật hướng đối tượng [còn được gọi là mẫu thiết kế]. Giả sử bạn có một đối tượng
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124 và muốn thay đổi hành vi của chỉ một trong các phương thức của nó. Bạn có thể tạo một lớp mới cung cấp cách triển khai mới cho phương thức mà bạn muốn thay đổi và ủy quyền tất cả các phương thức khác cho phương thức tương ứng của
>>> x = 10 >>> def foo[]: .. print[x] .. x += 124
Python programmers can easily implement delegation. Ví dụ, lớp sau triển khai một lớp hoạt động như một tệp nhưng chuyển đổi tất cả dữ liệu được viết thành chữ hoa
>>> x = 8 >>> squares[2][] 648
Ở đây, lớp
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1077 định nghĩa lại phương thức
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1078 để chuyển đổi chuỗi đối số thành chữ hoa trước khi gọi phương thức
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1079 bên dưới. Tất cả các phương thức khác được ủy quyền cho đối tượng
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1080 bên dưới. Việc ủy quyền được thực hiện thông qua phương pháp;
Lưu ý rằng đối với các trường hợp tổng quát hơn, việc ủy quyền có thể phức tạp hơn. Khi các thuộc tính phải được đặt cũng như được truy xuất, lớp cũng phải định nghĩa một phương thức và nó phải thực hiện cẩn thận. Việc triển khai cơ bản của
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1082 gần tương đương như sau
>>> x = 8 >>> squares[2][] 649
Hầu hết các triển khai
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1082 phải sửa đổi để tự lưu trữ trạng thái cục bộ mà không gây ra đệ quy vô hạn
Sử dụng chức năng tích hợp
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]0
Trong ví dụ này, sẽ tự động xác định phiên bản mà nó được gọi [giá trị
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1088], tra cứu [MRO] với
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1089 và trả về dòng tiếp theo sau
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1090 trong MRO.
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1091
Bạn có thể gán lớp cơ sở cho một bí danh và lấy từ bí danh. Sau đó, tất cả những gì bạn phải thay đổi là giá trị được gán cho bí danh. Ngẫu nhiên, thủ thuật này cũng hữu ích nếu bạn muốn quyết định động [e. g. tùy thuộc vào sự sẵn có của tài nguyên] nên sử dụng lớp cơ sở nào. Thí dụ
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]1
Cả dữ liệu tĩnh và phương thức tĩnh [theo nghĩa của C++ hoặc Java] đều được hỗ trợ trong Python
Đối với dữ liệu tĩnh, chỉ cần xác định thuộc tính lớp. Để gán một giá trị mới cho thuộc tính, bạn phải sử dụng rõ ràng tên lớp trong phép gán
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]2
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1092 cũng đề cập đến
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1093 đối với bất kỳ
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1094 nào mà
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1095 nắm giữ, trừ khi bị ghi đè bởi chính
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1094 hoặc bởi một số lớp trên đường tìm kiếm lớp cơ sở từ
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1097 trở lại
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1098
thận trọng. trong một phương thức của C, một phép gán như
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1099 tạo ra một thể hiện mới và không liên quan có tên là "đếm" theo lệnh của chính
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1088. Rebinding tên dữ liệu tĩnh của lớp phải luôn chỉ định lớp cho dù có bên trong phương thức hay không
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]3
Phương pháp tĩnh là có thể
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]4
Tuy nhiên, một cách đơn giản hơn nhiều để có được tác dụng của một phương thức tĩnh là thông qua một hàm cấp mô-đun đơn giản
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]5
Nếu mã của bạn được cấu trúc để xác định một lớp [hoặc hệ thống phân cấp lớp có liên quan chặt chẽ] cho mỗi mô-đun, điều này sẽ cung cấp khả năng đóng gói mong muốn
Câu trả lời này thực sự áp dụng cho tất cả các phương thức, nhưng câu hỏi thường xuất hiện đầu tiên trong ngữ cảnh của hàm tạo
Trong C++ bạn sẽ viết
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]6
Trong Python, bạn phải viết một hàm tạo duy nhất nắm bắt tất cả các trường hợp bằng các đối số mặc định. Ví dụ
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]7
Điều này không hoàn toàn tương đương, nhưng đủ gần trong thực tế
Bạn cũng có thể thử danh sách đối số có độ dài thay đổi, e. g
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]8
Cách tiếp cận tương tự hoạt động cho tất cả các định nghĩa phương thức
Các tên biến có hai dấu gạch dưới ở đầu được "xáo trộn" để cung cấp một cách đơn giản nhưng hiệu quả để xác định các biến riêng của lớp. Bất kỳ mã định danh nào có dạng
>>> print[x] 1101 [ít nhất hai dấu gạch dưới ở đầu, nhiều nhất là một dấu gạch dưới ở cuối] được thay thế bằng văn bản bằng
>>> print[x] 1102, trong đó
>>> print[x] 1103 là tên lớp hiện tại với mọi dấu gạch dưới ở đầu bị loại bỏ
Điều này không đảm bảo quyền riêng tư. người dùng bên ngoài vẫn có thể cố ý truy cập vào thuộc tính “_classname__spam” và các giá trị riêng tư được hiển thị trong đối tượng
>>> print[x] 1104. Nhiều lập trình viên Python không bao giờ bận tâm đến việc sử dụng tên biến riêng
Có một số lý do có thể cho việc này
Câu lệnh không nhất thiết phải gọi - nó chỉ đơn giản là giảm số lượng tham chiếu của đối tượng và nếu giá trị này bằng 0 thì
>>> print[x] 1106 được gọi
Nếu cấu trúc dữ liệu của bạn chứa các liên kết vòng [e. g. một cây trong đó mỗi đứa trẻ có một tham chiếu gốc và mỗi cha mẹ có một danh sách con] thì số lượng tham chiếu sẽ không bao giờ trở về 0. Thỉnh thoảng, Python chạy một thuật toán để phát hiện các chu kỳ như vậy, nhưng bộ thu gom rác có thể chạy một thời gian sau khi tham chiếu cuối cùng đến cấu trúc dữ liệu của bạn biến mất, vì vậy phương thức
>>> print[x] 1106 của bạn có thể được gọi vào thời điểm bất tiện và ngẫu nhiên. Điều này thật bất tiện nếu bạn đang cố tái tạo sự cố. Tồi tệ hơn, thứ tự thực hiện các phương thức của đối tượng
>>> print[x] 1106 là tùy ý. Bạn có thể chạy để bắt buộc thu thập, nhưng có những trường hợp bệnh lý mà các đối tượng sẽ không bao giờ được thu thập
Mặc dù có trình thu thập chu trình, bạn vẫn nên xác định một phương thức rõ ràng
>>> print[x] 1111 trên các đối tượng sẽ được gọi bất cứ khi nào bạn hoàn thành chúng. Phương thức
>>> print[x] 1111 sau đó có thể xóa các thuộc tính đề cập đến các đối tượng con. Đừng gọi trực tiếp
>>> print[x] 1106 –
>>> print[x] 1106 nên gọi
>>> print[x] 1111 và
>>> print[x] 1111 phải đảm bảo rằng nó có thể được gọi nhiều lần cho cùng một đối tượng
Một cách khác để tránh các tham chiếu theo chu kỳ là sử dụng mô-đun, cho phép bạn trỏ đến các đối tượng mà không cần tăng số lượng tham chiếu của chúng. Ví dụ, các cấu trúc dữ liệu dạng cây nên sử dụng các tham chiếu yếu cho các tham chiếu cha và anh chị em của chúng [nếu chúng cần. ]
Finally, if your
>>> print[x] 1106 method raises an exception, a warning message is printed to
Python không theo dõi tất cả các phiên bản của một lớp [hoặc của một kiểu dựng sẵn]. Bạn có thể lập trình hàm tạo của lớp để theo dõi tất cả các phiên bản bằng cách giữ một danh sách các tham chiếu yếu cho từng phiên bản
Nội dung trả về một số nguyên được đảm bảo là duy nhất trong suốt thời gian tồn tại của đối tượng. Vì trong CPython, đây là địa chỉ bộ nhớ của đối tượng, điều thường xảy ra là sau khi một đối tượng bị xóa khỏi bộ nhớ, đối tượng mới được tạo tiếp theo sẽ được cấp phát ở cùng một vị trí trong bộ nhớ. Điều này được minh họa bằng ví dụ này
>>> squares = [] >>> for x in range[5]: .. squares.append[lambda n=x: n**2]9
Hai id thuộc về các đối tượng số nguyên khác nhau được tạo trước đó và bị xóa ngay sau khi thực hiện lệnh gọi
>>> x = 10 >>> def foo[]: .. print[x] .. x += 119. Để chắc chắn rằng các đối tượng có id mà bạn muốn kiểm tra vẫn còn tồn tại, hãy tạo một tham chiếu khác đến đối tượng
>>> x = 10 >>> def foo[]: .. print[x] .. x += 100
Toán tử
>>> foo[] Traceback [most recent call last]: ... UnboundLocalError: local variable 'x' referenced before assignment22 kiểm tra danh tính đối tượng. Bài kiểm tra
>>> print[x] 1124 tương đương với
>>> print[x] 1125
Thuộc tính quan trọng nhất của kiểm tra danh tính là một đối tượng luôn đồng nhất với chính nó,
>>> print[x] 1126 luôn trả về
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1072. Kiểm tra danh tính thường nhanh hơn kiểm tra đẳng thức. Và không giống như các bài kiểm tra đẳng thức, các bài kiểm tra danh tính được đảm bảo trả về giá trị boolean
>>> x = 10 >>> def foobar[]: .. global x .. print[x] .. x += 1 ... >>> foobar[] 1072 hoặc
>>> print[x] 1129
Tuy nhiên, các bài kiểm tra nhận dạng chỉ có thể được thay thế cho các bài kiểm tra đẳng thức khi nhận dạng đối tượng được đảm bảo. Nói chung, có ba trường hợp mà danh tính được đảm bảo
1] Bài tập tạo tên mới nhưng không thay đổi nhận dạng đối tượng. Sau khi giao nhiệm vụ
>>> print[x] 1130, đảm bảo rằng
>>> print[x] 1131
2] Đặt một đối tượng vào vùng chứa lưu trữ các tham chiếu đối tượng không thay đổi danh tính đối tượng. Sau khi gán danh sách
>>> print[x] 1132, đảm bảo rằng
>>> print[x] 1133
3] Nếu một đối tượng là một singleton, điều đó có nghĩa là chỉ có thể tồn tại một phiên bản của đối tượng đó. Sau các nhiệm vụ
>>> print[x] 1134 và
>>> print[x] 1135, đảm bảo rằng
>>> print[x] 1124 vì
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165 là một người độc thân
Trong hầu hết các trường hợp khác, các bài kiểm tra danh tính là không nên và các bài kiểm tra bình đẳng được ưa thích hơn. Cụ thể, không nên sử dụng các kiểm tra nhận dạng để kiểm tra các hằng số như và không được đảm bảo là đơn lẻ
>>> x = 10 >>> def foo[]: .. print[x] .. x += 101
Tương tự như vậy, các phiên bản mới của vùng chứa có thể thay đổi không bao giờ giống hệt nhau
>>> x = 10 >>> def foo[]: .. print[x] .. x += 102
Trong mã thư viện tiêu chuẩn, bạn sẽ thấy một số mẫu phổ biến để sử dụng chính xác các bài kiểm tra danh tính
1] Theo khuyến nghị của PEP 8, kiểm tra danh tính là cách ưu tiên để kiểm tra
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165. Điều này đọc giống như tiếng Anh đơn giản trong mã và tránh nhầm lẫn với các đối tượng khác có thể có giá trị boolean đánh giá sai
2] Việc phát hiện các đối số tùy chọn có thể phức tạp khi
>>> x = 10 >>> def foo[]: .. print[x] .. x += 165 là giá trị đầu vào hợp lệ. Trong những tình huống đó, bạn có thể tạo một đối tượng canh gác đơn lẻ được đảm bảo khác biệt với các đối tượng khác. Ví dụ: đây là cách triển khai một phương thức hoạt động như
>>> x = 10 >>> def foo[]: .. print[x] .. x += 103
3] Việc triển khai vùng chứa đôi khi cần tăng cường kiểm tra tính bình đẳng bằng kiểm tra danh tính. Điều này giúp mã không bị nhầm lẫn bởi các đối tượng như
>>> print[x] 1143 không bằng chính chúng
Ví dụ, đây là triển khai của
>>> print[x] 1144
>>> x = 10 >>> def foo[]: .. print[x] .. x += 104
Khi phân lớp một loại bất biến, hãy ghi đè phương thức thay vì phương thức. Cái sau chỉ chạy sau khi một phiên bản được tạo, quá muộn để thay đổi dữ liệu trong một phiên bản không thay đổi
Tất cả các lớp bất biến này có chữ ký khác với lớp cha của chúng
>>> x = 10 >>> def foo[]: .. print[x] .. x += 105
Các lớp có thể được sử dụng như thế này
>>> x = 10 >>> def foo[]: .. print[x] .. x += 106
Hai công cụ chính cho các phương pháp lưu trữ là và. Cái trước lưu trữ kết quả ở cấp độ thể hiện và cái sau ở cấp độ lớp
Cách tiếp cận cached_property chỉ hoạt động với các phương thức không nhận bất kỳ đối số nào. Nó không tạo một tham chiếu đến thể hiện. Kết quả của phương thức được lưu trong bộ nhớ cache sẽ chỉ được lưu giữ miễn là phiên bản còn tồn tại
Ưu điểm là khi một instance không còn được sử dụng, kết quả của phương thức đã lưu trong bộ nhớ cache sẽ được giải phóng ngay lập tức. Nhược điểm là nếu các trường hợp tích lũy, thì phương thức tích lũy cũng sẽ dẫn đến kết quả. Họ có thể phát triển mà không bị ràng buộc
Cách tiếp cận lru_cache hoạt động với các phương thức có đối số có thể băm. It creates a reference to the instance unless special efforts are made to pass in weak references
Ưu điểm của thuật toán ít được sử dụng gần đây nhất là bộ đệm được giới hạn bởi kích thước tối đa được chỉ định. Điểm bất lợi là các phiên bản được giữ nguyên cho đến khi hết bộ nhớ cache hoặc cho đến khi bộ nhớ cache bị xóa
Ví dụ này cho thấy các kỹ thuật khác nhau
>>> x = 10 >>> def foo[]: .. print[x] .. x += 107
Ví dụ trên giả định rằng station_id không bao giờ thay đổi. Nếu các thuộc tính phiên bản có liên quan có thể thay đổi, thì phương pháp cached_property không thể hoạt động vì nó không thể phát hiện các thay đổi đối với các thuộc tính
Để làm cho cách tiếp cận lru_cache hoạt động khi station_id có thể thay đổi, lớp cần xác định các phương thức và để bộ đệm có thể phát hiện các cập nhật thuộc tính có liên quan
>>> x = 10 >>> def foo[]: .. print[x] .. x += 108
Khi một mô-đun được nhập lần đầu tiên [hoặc khi tệp nguồn đã thay đổi kể từ khi tệp được biên dịch hiện tại được tạo], tệp
>>> print[x] 1151 chứa mã đã biên dịch sẽ được tạo trong thư mục con
>>> print[x] 1152 của thư mục chứa tệp ________7____53. Tệp
>>> print[x] 1151 sẽ có tên tệp bắt đầu bằng tên giống như tệp
>>> print[x] 1153 và kết thúc bằng
>>> print[x] 1151, với thành phần ở giữa phụ thuộc vào mã nhị phân cụ thể của
>>> print[x] 1157 đã tạo ra nó. [Xem PEP 3147 để biết chi tiết. ]
Một lý do khiến tệp
>>> print[x] 1151 có thể không được tạo là do vấn đề về quyền đối với thư mục chứa tệp nguồn, nghĩa là không thể tạo thư mục con
>>> print[x] 1152. Ví dụ: điều này có thể xảy ra nếu bạn phát triển với tư cách người dùng này nhưng chạy với tư cách người dùng khác, chẳng hạn như nếu bạn đang thử nghiệm với máy chủ web
Trừ khi biến môi trường được đặt, việc tạo một. tệp pyc là tự động nếu bạn đang nhập mô-đun và Python có khả năng [quyền, dung lượng trống, v.v.] để tạo thư mục con
>>> print[x] 1152 và ghi mô-đun đã biên dịch vào thư mục con đó
Chạy Python trên tập lệnh cấp cao nhất không được coi là nhập và sẽ không có
>>> print[x] 1151 nào được tạo. Ví dụ: nếu bạn có một mô-đun cấp cao nhất
>>> print[x] 1163 nhập một mô-đun khác
>>> print[x] 1164, khi bạn chạy
>>> print[x] 1165 [bằng cách nhập
>>> print[x] 1166 dưới dạng lệnh trình bao], một
>>> print[x] 1151 sẽ được tạo cho
>>> print[x] 1168 vì
>>> print[x] 1168 được nhập, nhưng sẽ không có tệp
>>> print[x] 1151 nào được nhập
Nếu bạn cần tạo tệp
>>> print[x] 1151 cho
>>> print[x] 1165 – nghĩa là tạo tệp
>>> print[x] 1151 cho một mô-đun không được nhập – bạn có thể sử dụng và mô-đun
Mô-đun có thể tự biên dịch bất kỳ mô-đun nào. Một cách là sử dụng hàm
>>> print[x] 1179 trong mô-đun đó một cách tương tác
>>> x = 10 >>> def foo[]: .. print[x] .. x += 109
Thao tác này sẽ ghi
>>> print[x] 1151 vào thư mục con
>>> print[x] 1152 ở cùng vị trí với
>>> print[x] 1163 [hoặc bạn có thể ghi đè lên thư mục đó bằng tham số tùy chọn
>>> print[x] 1183]
Bạn cũng có thể tự động biên dịch tất cả các tệp trong một thư mục hoặc thư mục bằng cách sử dụng mô-đun. Bạn có thể làm điều đó từ dấu nhắc trình bao bằng cách chạy
>>> print[x] 1185 và cung cấp đường dẫn của một thư mục chứa các tệp Python để biên dịch
>>> x = 10 >>> def foo[]: .. print[x] .. x += 110
Một mô-đun có thể tìm ra tên mô-đun của chính nó bằng cách nhìn vào biến toàn cục được xác định trước
>>> print[x] 1186. Nếu giá trị này có giá trị
>>> print[x] 1187, chương trình đang chạy dưới dạng tập lệnh. Nhiều mô-đun thường được sử dụng bằng cách nhập chúng cũng cung cấp giao diện dòng lệnh hoặc tự kiểm tra và chỉ thực thi mã này sau khi kiểm tra
>>> print[x] 1186
>>> x = 10 >>> def foo[]: .. print[x] .. x += 111
Giả sử bạn có các mô-đun sau
>>> print[x] 1163
>>> x = 10 >>> def foo[]: .. print[x] .. x += 112
>>> print[x] 1190
>>> x = 10 >>> def foo[]: .. print[x] .. x += 113
Vấn đề là trình thông dịch sẽ thực hiện các bước sau
hàng nhập khẩu chính
>>> print[x] 11
65Toàn cầu trống cho
>>> print[x] 11
65 được tạo>>> print[x] 11
65 được biên dịch và bắt đầu thực thi>>> print[x] 11
65 nhập khẩu>>> print[x] 11
95Toàn cầu trống cho
>>> print[x] 11
95 được tạo>>> print[x] 11
95 được biên dịch và bắt đầu thực thi>>> print[x] 11
95 nhập khẩu>>> print[x] 11
65 [không hoạt động vì đã có một mô-đun tên là>>> print[x] 11
65]Cơ chế nhập cố gắng đọc
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 11
01 từ>>> print[x] 11
65 toàn cầu, để đặt>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 11
03
Bước cuối cùng không thành công, vì Python vẫn chưa hoàn thành việc diễn giải
>>> print[x] 1165 và từ điển ký hiệu chung cho
>>> print[x] 1165 vẫn còn trống
Điều tương tự cũng xảy ra khi bạn sử dụng
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 1106, sau đó thử truy cập vào
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 1107 trong mã chung
Có [ít nhất] ba cách giải quyết khả thi cho sự cố này
Guido van Rossum khuyên bạn nên tránh tất cả việc sử dụng
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 1108 và đặt tất cả mã bên trong các chức năng. Việc khởi tạo biến toàn cục và biến lớp chỉ nên sử dụng hằng hoặc hàm tích hợp. Điều này có nghĩa là mọi thứ từ mô-đun đã nhập được tham chiếu là
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 1109
Jim Roskind gợi ý thực hiện các bước theo thứ tự sau trong mỗi mô-đun
xuất [toàn cục, hàm và lớp không cần các lớp cơ sở đã nhập]
>>> def foo[]: .. x = 10 .. def bar[]: .. nonlocal x .. print[x] .. x += 1 .. bar[] .. print[x] ... >>> foo[] 10 11
10 câu nóimã hoạt động [bao gồm toàn cầu được khởi tạo từ các giá trị đã nhập]
Van Rossum không thích cách tiếp cận này lắm vì hàng nhập khẩu xuất hiện ở một nơi xa lạ, nhưng nó hoạt động
Matthias Urlichs khuyên bạn nên cấu trúc lại mã của mình để không cần nhập đệ quy ngay từ đầu
Các giải pháp này không loại trừ lẫn nhau
Thay vào đó, hãy xem xét sử dụng chức năng tiện lợi từ
>>> x = 10 >>> def foo[]: .. print[x] .. x += 114
For reasons of efficiency as well as consistency, Python only reads the module file on the first time a module is imported. Nếu không, trong một chương trình bao gồm nhiều mô-đun mà mỗi mô-đun nhập cùng một mô-đun cơ bản, mô-đun cơ bản sẽ được phân tích cú pháp và phân tích lại nhiều lần. Để buộc đọc lại một mô-đun đã thay đổi, hãy làm điều này
>>> x = 10 >>> def foo[]: .. print[x] .. x += 115
Cảnh báo. this technique is not 100% fool-proof. Cụ thể, các mô-đun chứa các câu lệnh như
>>> x = 10 >>> def foo[]: .. print[x] .. x += 116
sẽ tiếp tục hoạt động với phiên bản cũ của các đối tượng đã nhập. Nếu mô-đun chứa các định nghĩa lớp, các thể hiện của lớp hiện tại sẽ không được cập nhật để sử dụng định nghĩa lớp mới. This can result in the following paradoxical behaviour