Danh sách trống bất biến Python

Phần lớn, Python nhằm mục đích trở thành một ngôn ngữ rõ ràng và nhất quán, tránh những bất ngờ. Tuy nhiên có một số trường hợp có thể gây nhầm lẫn cho người mới

Một số trường hợp này là cố ý nhưng có thể gây bất ngờ. Một số có thể được coi là mụn cóc ngôn ngữ. Nói chung, những gì tiếp theo là một tập hợp các hành vi có khả năng gian xảo thoạt nhìn có vẻ kỳ lạ, nhưng nhìn chung là hợp lý, một khi bạn nhận thức được nguyên nhân cơ bản của sự ngạc nhiên

Đối số mặc định có thể thay đổi

Có vẻ như điều ngạc nhiên phổ biến nhất mà các lập trình viên Python mới gặp phải là cách Python xử lý các đối số mặc định có thể thay đổi trong các định nghĩa hàm

Những gì bạn đã viết

def append_to(element, to=[]):
    to.append(element)
    return to

Những gì bạn có thể mong đợi sẽ xảy ra

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)

Một danh sách mới được tạo mỗi khi hàm được gọi nếu đối số thứ hai không được cung cấp, do đó đầu ra là

[12]
[42]

Điều gì thực sự xảy ra

[12]
[12, 42]

Một danh sách mới được tạo một lần khi chức năng được xác định và cùng một danh sách được sử dụng trong mỗi lần gọi liên tiếp

Các đối số mặc định của Python được đánh giá một lần khi hàm được xác định, không phải mỗi khi hàm được gọi (giống như trong Ruby). Điều này có nghĩa là nếu bạn sử dụng một đối số mặc định có thể thay đổi và thay đổi nó, thì bạn cũng sẽ và đã thay đổi đối tượng đó cho tất cả các lệnh gọi hàm trong tương lai.

Thay vào đó bạn nên làm gì

Tạo một đối tượng mới mỗi khi hàm được gọi, bằng cách sử dụng một đối số mặc định để báo hiệu rằng không có đối số nào được cung cấp (thường là một lựa chọn tốt)

def append_to(element, to=None):
    if to is None:
        to = []
    to.append(element)
    return to

Đừng quên, bạn đang chuyển một đối tượng danh sách làm đối số thứ hai

Khi Gotcha không phải là Gotcha

Đôi khi bạn có thể “khai thác” cụ thể (đọc. sử dụng như dự định) hành vi này để duy trì trạng thái giữa các lần gọi hàm. Điều này thường được thực hiện khi viết chức năng lưu trữ

Đóng ràng buộc muộn

Một nguồn gây nhầm lẫn phổ biến khác là cách Python liên kết các biến của nó trong các bao đóng (hoặc trong phạm vi toàn cầu xung quanh)

Những gì bạn đã viết

def create_multipliers():
    return [lambda x : i * x for i in range(5)]

Những gì bạn có thể mong đợi sẽ xảy ra

for multiplier in create_multipliers():
    print(multiplier(2))

Một danh sách chứa năm hàm mà mỗi hàm có biến số đóng của riêng chúng

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
6 để nhân đối số của chúng, tạo ra

________số 8_______

Điều gì thực sự xảy ra

8
8
8
8
8

Năm chức năng được tạo ra;

Các bao đóng của Python bị ràng buộc muộn. Điều này có nghĩa là giá trị của các biến được sử dụng trong bao đóng được tra cứu tại thời điểm hàm bên trong được gọi

Tại đây, bất cứ khi nào bất kỳ hàm trả về nào được gọi, giá trị của

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
6 sẽ được tra cứu trong phạm vi xung quanh tại thời điểm gọi. Đến lúc đó, vòng lặp đã hoàn thành và chỉ còn lại
my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
6 với giá trị cuối cùng là 4

Điều đặc biệt khó chịu về gotcha này là thông tin sai lệch dường như phổ biến rằng điều này có liên quan đến Python. Các hàm được tạo bằng biểu thức

[12]
[42]
0 không có gì đặc biệt và trên thực tế, hành vi chính xác tương tự được thể hiện bằng cách chỉ sử dụng một
[12]
[42]
1 bình thường

def create_multipliers():
    multipliers = []

    for i in range(5):
        def multiplier(x):
            return i * x
        multipliers.append(multiplier)

    return multipliers

Thay vào đó bạn nên làm gì

Giải pháp chung nhất được cho là một chút hack. Do hành vi đã nói ở trên của Python liên quan đến việc đánh giá các đối số mặc định cho các hàm (xem ), bạn có thể tạo một bao đóng liên kết ngay lập tức với các đối số của nó bằng cách sử dụng một đối số mặc định như vậy

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
0

Ngoài ra, bạn có thể sử dụng funcools. chức năng từng phần

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
1

Khi Gotcha không phải là Gotcha

Đôi khi bạn muốn các lần đóng của mình hoạt động theo cách này. Ràng buộc muộn là tốt trong nhiều tình huống. Thật không may, vòng lặp để tạo các chức năng duy nhất là một trường hợp mà chúng có thể gây ra trục trặc

Mã byte (. pyc) Tệp ở mọi nơi

Theo mặc định, khi thực thi mã Python từ các tệp, trình thông dịch Python sẽ tự động ghi phiên bản mã byte của tệp đó vào đĩa, e. g.

[12]
[42]
2

Không nên kiểm tra các tệp

[12]
[42]
3 này trong kho mã nguồn của bạn

Về mặt lý thuyết, hành vi này được bật theo mặc định vì lý do hiệu suất. Nếu không có các tệp mã byte này, Python sẽ tạo lại mã byte mỗi khi tệp được tải

Vô hiệu hóa mã byte (. pyc) Tệp

May mắn thay, quá trình tạo mã byte diễn ra cực kỳ nhanh và bạn không cần phải lo lắng khi phát triển mã của mình

Những tệp đó gây phiền nhiễu, vì vậy hãy loại bỏ chúng

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
2

Với bộ biến môi trường

[12]
[42]
4, Python sẽ không ghi các tệp này vào đĩa nữa và môi trường phát triển của bạn sẽ vẫn đẹp và sạch sẽ

Tôi khuyên bạn nên đặt biến môi trường này trong

[12]
[42]
5 của mình

Xóa mã byte (. pyc) Tệp

Đây là mẹo hay để xóa tất cả các tệp này, nếu chúng đã tồn tại

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
3

Chạy nó từ thư mục gốc của dự án của bạn và tất cả các tệp

[12]
[42]
3 sẽ đột nhiên biến mất. Tốt hơn nhiều

Kiểm soát phiên bản bỏ qua

Nếu bạn vẫn cần tệp

[12]
[42]
3 vì lý do hiệu suất, bạn luôn có thể thêm chúng vào tệp bỏ qua của kho lưu trữ kiểm soát phiên bản của mình. Các hệ thống kiểm soát phiên bản phổ biến có khả năng sử dụng các ký tự đại diện được xác định trong tệp để áp dụng các quy tắc đặc biệt

Tệp bỏ qua sẽ đảm bảo các tệp phù hợp không được kiểm tra vào kho lưu trữ. Git sử dụng

[12]
[42]
8 trong khi Mercurial sử dụng
[12]
[42]
9

Ở mức tối thiểu, các tệp bỏ qua của bạn sẽ trông như thế này

my_list = append_to(12)
print(my_list)

my_other_list = append_to(42)
print(my_other_list)
4

Bạn có thể muốn bao gồm nhiều tệp và thư mục hơn tùy thuộc vào nhu cầu của bạn. Lần tới khi bạn cam kết với kho lưu trữ, các tệp này sẽ không được đưa vào

Danh sách trống trong Python là gì?

Trong python, danh sách trống được định nghĩa là danh sách không có phần tử hoặc mục nào trong danh sách . Để xác định một danh sách trống trong Python, có hai cách để thực hiện việc này và chúng được thực hiện bằng cách sử dụng dấu ngoặc vuông [] hoặc bằng cách sử dụng hàm tạo danh sách như list().

Tại sao bạn không nên biến các đối số mặc định thành một danh sách trống Python?

Đây không phải là hành vi mà chúng tôi mong muốn. Một danh sách mới được tạo một lần khi chức năng được xác định và cùng một danh sách được sử dụng trong mỗi lần gọi liên tiếp. Các đối số mặc định của Python được đánh giá một lần khi hàm được xác định, không phải mỗi lần hàm được gọi .

Tại sao không nên đặt tham số hàm dưới dạng danh sách trống làm giá trị mặc định?

Trả lời. d) là một ý tưởng tồi vì mặc định [] sẽ tích lũy dữ liệu và mặc định [] sẽ thay đổi với các lần gọi tiếp theo .

Sự khác biệt giữa trống và không có trong danh sách Python là gì?

Thông thường, danh sách trống có ý nghĩa khác với Không có ; . None means no value while an empty list means zero values.