Khai báo biến trong Python


85

Học Python , và có một số nghi ngờ cơ bản.

1. Tôi đã thấy khai báo biến (đường dẫn ở đây) là

class writer:
    path = ""

đôi khi, không có khai báo rõ ràng nhưng khởi tạo thông qua __init__.

def __init__(self, name):
    self.name = name

Tôi hiểu mục đích của __init__, nhưng có nên khai báo biến trong bất kỳ hàm nào khác không.

2.Làm cách nào để tạo biến để giữ một kiểu tùy chỉnh?

class writer:
    path = "" # string value
    customObj = ??

12
Và, liên quan đến câu hỏi thứ 2: trong Python, các biến không có kiểu: giá trị thì có. Vì vậy, bất kỳ biến nào cũng có thể chứa các đối tượng tùy chỉnh của bạn.
jpaugh

4
Nó sẽ hữu ích nếu bạn ngừng nghĩ về tên / biểu tượng là các biến . Họ là những tài liệu tham khảo cho các đối tượng
John La Rooy

2
tên @gnibbler Hoặc cho họ ...
detly

1
@detly, tôi nghĩ tên là một lựa chọn tồi. Những điều thường chỉ có một tên trong khi một đối tượng có thể có nhiều tài liệu tham khảo
John La Rooy

2
@JohnClements, nhưng python không hoạt động như toán học. Nếu bạn muốn hiểu python, bạn cần biết rằng các đối tượng có __name__thuộc tính có "tên". Không phải tất cả các đối tượng đều có một __name__thuộc tính, nhưng bạn vẫn có thể có các tham chiếu đến chúng. Nếu chúng ta bắt đầu gọi một "tài liệu tham khảo" là một "tên" thì chúng ta đang làm cho những người mới bắt đầu trở thành kẻ lạc lối.
John La Rooy

Câu trả lời:


202

Được rồi, điều đầu tiên trước tiên.

Không có cái gọi là "khai báo biến" hoặc "khởi tạo biến" trong Python.

Đơn giản là chúng ta gọi là "chuyển nhượng", nhưng có lẽ chỉ nên gọi "đặt tên".

Phép gán có nghĩa là "tên này ở phía bên trái bây giờ đề cập đến kết quả đánh giá phía bên phải, bất kể nó đã đề cập đến những gì trước đây (nếu có)".

foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication

Do đó, tên của Python (một thuật ngữ tốt hơn "biến", được cho là) ​​không có các kiểu liên kết; các giá trị nào. Bạn có thể áp dụng lại cùng một tên cho bất kỳ thứ gì bất kể loại của nó là gì, nhưng thứ đó vẫn có hành vi phụ thuộc vào loại của nó. Tên chỉ đơn giản là một cách để tham chiếu đến giá trị (đối tượng). Điều này trả lời câu hỏi thứ hai của bạn: Bạn không tạo biến để giữ một loại tùy chỉnh. Bạn không tạo biến để giữ bất kỳ kiểu cụ thể nào. Bạn không "tạo" biến nào cả. Bạn đặt tên cho các đối tượng.

Điểm thứ hai: Python tuân theo một quy tắc rất đơn giản khi nói đến các lớp, điều đó thực sự nhất quán hơn nhiều so với những gì các ngôn ngữ như Java, C ++ và C # làm: mọi thứ được khai báo bên trong classkhối đều là một phần của lớp . Vì vậy, các hàm ( def) được viết ở đây là các phương thức, tức là một phần của đối tượng lớp (không được lưu trữ trên cơ sở từng trường hợp), giống như trong Java, C ++ và C #; nhưng các tên khác ở đây cũng là một phần của lớp. Một lần nữa, các tên chỉ là tên và chúng không có các kiểu liên kết và các hàm cũng là các đối tượng trong Python. Như vậy:

class Example:
    data = 42
    def method(self): pass

Các lớp cũng là các đối tượng , trong Python.

Vì vậy, bây giờ chúng ta đã tạo một đối tượng có tên Example, đại diện cho lớp của tất cả những thứ là Examples. Đối tượng này có hai thuộc tính do người dùng cung cấp (Trong C ++, "thành viên"; trong C #, "trường hoặc thuộc tính hoặc phương thức"; trong Java, "trường hoặc phương thức"). Một trong số chúng được đặt tên datavà nó lưu giá trị số nguyên 42. Người khác được đặt tênmethod và nó lưu trữ một đối tượng hàm. (Có một số thuộc tính khác mà Python tự động thêm vào.)

Tuy nhiên, những thuộc tính này vẫn không thực sự là một phần của đối tượng. Về cơ bản, một đối tượng chỉ là một tập hợp nhiều tên hơn (tên thuộc tính), cho đến khi bạn chuyển sang những thứ không thể chia ra được nữa. Do đó, các giá trị có thể được chia sẻ giữa các trường hợp khác nhau của một lớp, hoặc thậm chí giữa các đối tượng của các lớp khác nhau, nếu bạn cố tình thiết lập điều đó.

Hãy tạo một phiên bản:

x = Example()

Bây giờ chúng ta có một đối tượng riêng biệt được đặt tên x, là một ví dụ của Example. Các datamethodthực sự không phải là một phần của đối tượng, nhưng chúng ta vẫn có thể tra cứu chúng xnhờ một số phép thuật mà Python thực hiện đằng sau hậu trường. methodĐặc biệt, khi chúng ta tra cứu , thay vào đó, chúng ta sẽ nhận được một "phương thức bị ràng buộc" (khi chúng ta gọi nó, nó xsẽ tự động được truyền dưới dạng selftham số, điều này không thể xảy ra nếu chúng ta tra cứu Example.methodtrực tiếp).

Điều gì xảy ra khi chúng ta cố gắng sử dụng x.data?

Khi chúng tôi kiểm tra nó, nó sẽ được nhìn lên đối tượng đầu tiên. Nếu nó không được tìm thấy trong đối tượng, Python sẽ tìm trong lớp.

Tuy nhiên, khi chúng ta gán cho x.data , Python sẽ tạo một thuộc tính trên đối tượng. Nó sẽ không thay thế thuộc tính class '.

Điều này cho phép chúng ta khởi tạo đối tượng . Python sẽ tự động gọi __init__phương thức của lớp trên các phiên bản mới khi chúng được tạo, nếu có. Trong phương pháp này, chúng ta có thể chỉ cần gán cho các thuộc tính để đặt các giá trị ban đầu cho thuộc tính đó trên mỗi đối tượng:

class Example:
    name = "Ignored"
    def __init__(self, name):
        self.name = name
    # rest as before

Bây giờ chúng ta phải chỉ định a namekhi chúng ta tạo một Example, và mỗi thể hiện có riêng của nó name. Python sẽ bỏ qua thuộc tính lớp Example.namebất cứ khi nào chúng ta tìm kiếm thuộc tính .namecủa một cá thể, vì thuộc tính của cá thể đó sẽ được tìm thấy đầu tiên.

Một lưu ý cuối cùng: sửa đổi (đột biến) và chuyển nhượng là những thứ khác nhau!

Trong Python, các chuỗi là bất biến. Chúng không thể được sửa đổi. Khi bạn làm:

a = 'hi '
b = a
a += 'mom'

Bạn không thay đổi chuỗi 'hi' ban đầu. Điều đó là không thể trong Python. Thay vào đó, bạn tạo một chuỗi mới'hi mom' và khiến anó không còn là tên nữa 'hi ', và 'hi mom'thay vào đó bắt đầu là tên . Chúng tôi đã bđặt tên cho 'hi 'cũng như sau khi áp dụng lại atên, bvẫn là một tên cho 'hi ', vì 'hi 'vẫn tồn tại và không bị thay đổi.

Nhưng danh sách có thể được thay đổi:

a = [1, 2, 3]
b = a
a += [4]

Bây giờ blà [1, 2, 3, 4], bởi vì chúng tôi đã bđặt tên cho cùng một thứ đã ađặt tên, và sau đó chúng tôi đã thay đổi thứ đó. Chúng tôi đã không tạo một danh sách mới ađể đặt tên, vì Python chỉ đơn giản là xử lý +=khác nhau cho các danh sách.

Điều này quan trọng đối với các đối tượng vì nếu bạn có một danh sách làm thuộc tính lớp và sử dụng một thể hiện để sửa đổi danh sách, thì thay đổi sẽ được "nhìn thấy" trong tất cả các trường hợp khác. Điều này là do (a) dữ liệu thực sự là một phần của đối tượng lớp chứ không phải bất kỳ đối tượng cá thể nào; (b) bởi vì bạn đang sửa đổi danh sách và không thực hiện một phép gán đơn giản, bạn đã không tạo một thuộc tính thể hiện mới ẩn thuộc tính lớp.


12
Đây là lý do tại sao chúng tôi gọi chúng là 'số nhận dạng' . :-)
Martijn Pieters

Cuối cùng, @Karl_Knechtel trong ví dụ chuỗi của bạn, điều gì sẽ xảy ra với 'hi' nếu chúng ta không bao giờ đặt tên nó là 'b'? Đây có phải là một rò rỉ bộ nhớ?
heretoinfinity

2
@heretoinfinity: không; Python sử dụng tính năng thu thập rác để giải phóng các đối tượng không thể truy cập
John Clements

Khai báo biến được giải thích rõ trong video này: youtube.com/watch?v=ao2N37E-D9s .
Ishaq Khan,

xem video này để biết câu trả lời youtube.com/…
Thao N

23

Điều này có thể trễ 6 năm, nhưng trong Python 3.5 trở lên, bạn khai báo một kiểu biến như sau:

variable_name: type_name

hoặc cái này:

variable_name # type: shinyType

Vì vậy, trong trường hợp của bạn (nếu bạn đã CustomObjectxác định một lớp), bạn có thể làm:

customObj: CustomObject

Xem cái này hoặc cái kia để biết thêm thông tin.


22

Không cần phải khai báo các biến mới trong Python. Nếu chúng ta đang nói về các biến trong hàm hoặc mô-đun, thì không cần khai báo. Chỉ cần gán giá trị cho một tên, nơi bạn cần nó: mymagic = "Magic". Các biến trong Python có thể giữ các giá trị thuộc bất kỳ loại nào và bạn không thể hạn chế điều đó.

Câu hỏi của bạn hỏi cụ thể về các lớp, đối tượng và các biến thể hiện. Cách thành ngữ để tạo biến cá thể là trong __init__phương thức và không ở đâu khác - trong khi bạn có thể tạo biến phiên bản mới trong các phương thức khác hoặc thậm chí trong mã không liên quan, đó chỉ là một ý tưởng tồi. Nó sẽ làm cho mã của bạn khó lý luận hoặc duy trì.

Ví dụ:

class Thing(object):

    def __init__(self, magic):
        self.magic = magic

Dễ dàng. Bây giờ các bản sao của lớp này có một magicthuộc tính:

thingo = Thing("More magic")
# thingo.magic is now "More magic"

Bản thân việc tạo các biến trong không gian tên của lớp dẫn đến các hành vi khác nhau hoàn toàn. Nó khác nhau về mặt chức năng và bạn chỉ nên làm điều đó nếu bạn có lý do cụ thể. Ví dụ:

class Thing(object):

    magic = "Magic"

    def __init__(self):
        pass

Bây giờ cố gắng:

thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1

Hoặc là:

class Thing(object):

    magic = ["More", "magic"]

    def __init__(self):
        pass

thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]

Điều này là do bản thân không gian tên của lớp khác với không gian tên của các đối tượng được tạo từ nó. Tôi sẽ giao nó cho bạn để nghiên cứu thêm một chút.

Thông báo mang về nhà là Python có nghĩa là (a) khởi tạo các thuộc tính đối tượng trong __init__phương thức của bạn và (b) ghi lại hành vi của lớp bạn nếu cần. Bạn không cần phải gặp rắc rối với tài liệu cấp Sphinx đầy đủ cho mọi thứ bạn từng viết, nhưng ít nhất một số nhận xét về bất kỳ chi tiết nào mà bạn hoặc người khác có thể cần phải tiếp thu.


12

Đối với mục đích xác định phạm vi, tôi sử dụng:

custom_object = None

1

Các biến có phạm vi, do đó, có các biến cụ thể cho hàm của bạn là điều phù hợp. Không phải lúc nào bạn cũng phải rõ ràng về định nghĩa của chúng; thường thì bạn chỉ có thể sử dụng chúng. Chỉ khi bạn muốn thực hiện điều gì đó cụ thể cho loại biến, chẳng hạn như nối thêm cho danh sách, bạn mới cần xác định chúng trước khi bắt đầu sử dụng. Ví dụ điển hình về điều này.

list = []
for i in stuff:
  list.append(i)

Nhân tiện, đây không thực sự là một cách tốt để thiết lập danh sách. Sẽ tốt hơn nếu nói:

list = [i for i in stuff] # list comprehension

...nhưng tôi lạc đề.

Câu hỏi khác của bạn. Đối tượng tùy chỉnh phải là một lớp.

class CustomObject(): # always capitalize the class name...this is not syntax, just style.
  pass
customObj = CustomObject()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.