การประกาศตัวแปร Python


87

กำลังเรียนรู้Pythonและมีข้อสงสัยเบื้องต้น

1. ฉันเห็นการประกาศตัวแปร (เส้นทางที่นี่) เป็น

class writer:
    path = ""

บางครั้งไม่มีการประกาศอย่างชัดเจน __init__แต่เริ่มต้นผ่าน

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

ฉันเข้าใจจุดประสงค์__init__แต่แนะนำให้ประกาศตัวแปรในฟังก์ชันอื่น ๆ หรือไม่

2. ฉันจะสร้างตัวแปรเพื่อเก็บประเภทที่กำหนดเองได้อย่างไร

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

12
และเกี่ยวกับคำถามที่ 2: ใน Python ตัวแปรไม่มี type: values ​​do ดังนั้นตัวแปรใด ๆ ก็สามารถเก็บวัตถุที่คุณกำหนดเองได้
jpaugh

4
มันจะช่วยถ้าคุณหยุดคิดชื่อ / idendifiers เป็นตัวแปร พวกเขาอ้างอิงถึงวัตถุ
John La Rooy

2
@gnibbler หรือชื่อสำหรับพวกเขา ...
detly

1
@ รายละเอียดฉันคิดว่าชื่อเป็นตัวเลือกที่ไม่ดี สิ่งต่างๆมักจะมีชื่อเดียวในขณะที่วัตถุอาจมีการอ้างอิงหลายรายการ
John La Rooy

2
@JohnClements แต่ python ไม่ทำงานเหมือนคณิตศาสตร์ หากคุณต้องการเข้าใจ python คุณจำเป็นต้องทราบว่าวัตถุที่มี__name__แอตทริบิวต์มี "ชื่อ" ไม่ใช่ทุกวัตถุที่มี__name__แอตทริบิวต์ แต่คุณยังอ้างอิงได้ หากเราเริ่มเรียก "ข้อมูลอ้างอิง" ว่า "ชื่อ" เรากำลังทำให้ผู้เริ่มต้นเสียประโยชน์ไปตามเส้นทาง
John La Rooy

คำตอบ:


205

เอาล่ะสิ่งแรกก่อน

ไม่มีสิ่งที่เรียกว่า "การประกาศตัวแปร" หรือ "การเริ่มต้นตัวแปร" ใน Python

มีเพียงสิ่งที่เราเรียกว่า "การมอบหมาย" แต่น่าจะเรียกว่า "การตั้งชื่อ"

การมอบหมายงานหมายถึง "ชื่อนี้ทางด้านซ้ายมือตอนนี้หมายถึงผลลัพธ์ของการประเมินด้านขวามือโดยไม่คำนึงถึงสิ่งที่อ้างถึงก่อนหน้านี้ (ถ้ามี)"

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

ด้วยเหตุนี้ชื่อของ Python (คำที่ดีกว่า "ตัวแปร" เนื้อหา) จึงไม่มีประเภทที่เกี่ยวข้อง ค่าทำ คุณสามารถใช้ชื่อเดิมซ้ำกับอะไรก็ได้โดยไม่คำนึงถึงประเภท แต่สิ่งนั้นยังคงมีพฤติกรรมที่ขึ้นอยู่กับประเภทของมัน ชื่อเป็นเพียงวิธีอ้างถึงค่า (วัตถุ) สิ่งนี้ตอบคำถามที่สองของคุณ: คุณไม่ได้สร้างตัวแปรเพื่อเก็บประเภทที่กำหนดเอง คุณไม่ได้สร้างตัวแปรเพื่อเก็บประเภทใดประเภทหนึ่ง คุณไม่ได้ "สร้าง" ตัวแปรเลย คุณตั้งชื่อให้กับวัตถุ

จุดที่สอง: งูหลามตามกฎง่ายมากเมื่อมันมาถึงการเรียนที่เป็นจริงมากขึ้นกว่าสิ่งที่สอดคล้องภาษาเช่น Java, C ++ และ C # ทำทุกอย่างที่ประกาศภายในclassบล็อกเป็นส่วนหนึ่งของการเรียน ดังนั้นฟังก์ชัน ( def) ที่เขียนในที่นี้คือเมธอดซึ่งเป็นส่วนหนึ่งของคลาสอ็อบเจ็กต์ (ไม่ได้จัดเก็บแบบต่ออินสแตนซ์) เช่นเดียวกับใน Java, C ++ และ C #; แต่ชื่ออื่น ๆก็เป็นส่วนหนึ่งของชั้นเรียนเช่นกัน อีกครั้งชื่อเป็นเพียงชื่อและไม่มีประเภทที่เกี่ยวข้องและฟังก์ชันก็เป็นวัตถุเช่นกันใน Python ดังนั้น:

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

คลาสเป็นวัตถุเช่นกันใน Python

ตอนนี้เราได้สร้างอ็อบเจกต์ชื่อExampleซึ่งแสดงถึงคลาสของทุกสิ่งที่เป็นExamples อ็อบเจ็กต์นี้มีแอ็ตทริบิวต์ที่ผู้ใช้จัดหาให้สองแอ็ตทริบิวต์ (ใน C ++ "สมาชิก" ใน C # "ฟิลด์หรือคุณสมบัติหรือเมธอด" ใน Java "ฟิลด์หรือเมธอด") หนึ่งในนั้นคือการตั้งชื่อและเก็บค่าจำนวนเต็มdata 42อีกตัวตั้งชื่อmethodและเก็บอ็อบเจ็กต์ฟังก์ชัน (มีคุณสมบัติอื่น ๆ อีกมากมายที่ Python เพิ่มโดยอัตโนมัติ)

แอตทริบิวต์เหล่านี้ยังคงไม่ได้เป็นส่วนหนึ่งของวัตถุจริงๆ โดยพื้นฐานแล้วออบเจ็กต์เป็นเพียงกลุ่มของชื่ออื่น ๆ (ชื่อแอตทริบิวต์) จนกว่าคุณจะเข้าใจสิ่งที่ไม่สามารถแบ่งออกได้อีก ดังนั้นค่าสามารถใช้ร่วมกันระหว่างอินสแตนซ์ต่างๆของคลาสหรือแม้กระทั่งระหว่างอ็อบเจ็กต์ของคลาสต่างๆหากคุณตั้งใจตั้งค่านั้น

มาสร้างอินสแตนซ์:

x = Example()

ตอนนี้เรามีออบเจ็กต์แยกต่างหากชื่อxซึ่งเป็นตัวอย่างของExample. dataและmethodไม่ได้เป็นส่วนหนึ่งของวัตถุ แต่เรายังคงสามารถมองพวกเขาผ่านทางxมายากลเพราะบางอย่างที่หลามไม่อยู่เบื้องหลัง methodโดยเฉพาะอย่างยิ่งเมื่อเราค้นหาเราจะได้ "วิธีการผูก" แทน (เมื่อเราเรียกมันจะxถูกส่งผ่านโดยอัตโนมัติเป็นselfพารามิเตอร์ซึ่งไม่สามารถเกิดขึ้นได้หากเราค้นหาExample.methodโดยตรง)

จะเกิดอะไรขึ้นเมื่อเราพยายามใช้x.data?

เมื่อเราตรวจสอบมันจะมองขึ้นไปในวัตถุก่อน หากไม่พบในวัตถุ Python จะดูในคลาส

อย่างไรก็ตามเมื่อเรากำหนดให้ x.data Python จะสร้างแอตทริบิวต์บนวัตถุ จะไม่แทนที่แอตทริบิวต์ของคลาส

สิ่งนี้ช่วยให้เราทำการเริ่มต้นอ็อบเจ็กต์ Python จะเรียก__init__เมธอดของคลาสโดยอัตโนมัติในอินสแตนซ์ใหม่เมื่อสร้างขึ้นหากมี ในวิธีนี้เราสามารถกำหนดให้กับแอตทริบิวต์เพื่อกำหนดค่าเริ่มต้นสำหรับแอตทริบิวต์นั้นในแต่ละวัตถุ:

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

ตอนนี้เราต้องระบุnameเมื่อเราสร้างและแต่ละกรณีมีของตัวเองExample namePython จะละเว้นแอตทริบิวต์คลาสExample.nameเมื่อใดก็ตามที่เราค้นหา.nameอินสแตนซ์เนื่องจากจะพบแอตทริบิวต์ของอินสแตนซ์ก่อน

ข้อแม้สุดท้าย: การดัดแปลง (การกลายพันธุ์) และการมอบหมายงานเป็นสิ่งที่แตกต่างกัน!

ใน Python สตริงไม่เปลี่ยนรูป ไม่สามารถแก้ไขได้ เมื่อคุณทำ:

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

คุณไม่ได้เปลี่ยนสตริง 'hi' ดั้งเดิม เป็นไปไม่ได้ใน Python แต่คุณสร้างสตริงใหม่'hi mom'และทำให้aหยุดเป็นชื่อสำหรับ'hi 'และเริ่มเป็นชื่อ'hi mom'แทน เราbตั้งชื่อให้'hi 'เช่นกันและหลังจากใช้aชื่อซ้ำแล้วbก็ยังคงเป็นชื่อ'hi 'เพราะ'hi 'ยังคงมีอยู่และไม่มีการเปลี่ยนแปลง

แต่รายการสามารถเปลี่ยนแปลงได้:

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

ตอนนี้bคือ [1, 2, 3, 4] เช่นกันเพราะเราbตั้งชื่อให้กับสิ่งเดียวกันที่aตั้งชื่อแล้วเราก็เปลี่ยนสิ่งนั้น เราไม่ได้สร้างรายการใหม่aเพื่อตั้งชื่อเนื่องจาก Python ปฏิบัติ+=แตกต่างกันไปสำหรับรายการ

สิ่งนี้มีความสำคัญสำหรับออบเจ็กต์เนื่องจากหากคุณมีรายการเป็นแอตทริบิวต์คลาสและใช้อินสแตนซ์เพื่อแก้ไขรายการการเปลี่ยนแปลงจะ "เห็น" ในอินสแตนซ์อื่น ๆ ทั้งหมด เนื่องจาก (ก) ข้อมูลเป็นส่วนหนึ่งของคลาสอ็อบเจ็กต์ไม่ใช่อ็อบเจ็กต์อินสแตนซ์ใด ๆ (b) เนื่องจากคุณกำลังแก้ไขรายการและไม่ได้ทำการมอบหมายอย่างง่ายคุณจึงไม่ได้สร้างแอตทริบิวต์อินสแตนซ์ใหม่โดยซ่อนแอตทริบิวต์คลาส


12
นี่คือเหตุผลที่เราเรียกว่า'ตัวระบุ' :-)
Martijn Pieters

@Karl_Knechtel ในตัวอย่างสตริงของคุณในท้ายที่สุดจะเกิดอะไรขึ้นกับ 'hi' ถ้าเราไม่เคยตั้งชื่อเป็น 'b' นี่คือความทรงจำรั่ว?
heretoinfinity

2
@heretoinfinity: ไม่นะ; Python ใช้การเก็บขยะเพื่อปลดปล่อยวัตถุที่ไม่สามารถเข้าถึงได้
John Clements

ประกาศตัวแปรจะอธิบายได้ดีในวิดีโอนี้: youtube.com/watch?v=ao2N37E-D9s
Ishaq Khan

ลองดูวิดีโอนี้เพื่อหาคำตอบyoutube.com/…
เฒ่าน

23

อาจช้าไป 6 ปี แต่ใน Python 3.5 ขึ้นไปคุณจะประกาศประเภทตัวแปรดังนี้:

variable_name: type_name

หรือสิ่งนี้:

variable_name # type: shinyType

ดังนั้นในกรณีของคุณ (ถ้าคุณมีCustomObjectคลาสที่กำหนดไว้) คุณสามารถทำได้:

customObj: CustomObject

ดูสิ่งนี้หรือสิ่งนั้นสำหรับข้อมูลเพิ่มเติม


นั่นไม่ใช่การประกาศในความหมายดั้งเดิม แต่เป็นคำใบ้ซึ่งตัวตรวจสอบชนิดคงที่และ IDE อาจชี้ให้เห็น แต่จะยังคงทำงานและล้มเหลว
PhilHibbs

@PhilHibbs มันไม่ได้ล้มเหลวแม้ว่า คำว่า "ล้มเหลว" หมายความว่าอย่างไร
O. Aroesti

สิ่งที่ฉันหมายถึงคือถ้าคุณทำa: intแล้วทำa='hello, world!'มันจะไม่ล้มเหลวดังนั้นจึงไม่ได้ประกาศว่าเป็น int และจะเป็น int เท่านั้น ฉันควรจะพูดว่า "มันจะไม่ล้มเหลว" ความเลวของฉัน หากคุณไม่a: int, a='hello, world!', a=a+1แล้วที่ควรจะล้มเหลวในการรวบรวมในภาษาคงพิมพ์
PhilHibbs

22

ไม่จำเป็นต้องประกาศตัวแปรใหม่ใน Python หากเรากำลังพูดถึงตัวแปรในฟังก์ชันหรือโมดูลไม่จำเป็นต้องมีการประกาศ mymagic = "Magic"เพียงแค่กำหนดค่าให้กับชื่อที่คุณต้องการ: ตัวแปรใน Python สามารถเก็บค่าประเภทใดก็ได้และคุณไม่สามารถ จำกัด ได้

คำถามของคุณถามเกี่ยวกับคลาสออบเจ็กต์และตัวแปรอินสแตนซ์โดยเฉพาะ วิธีการสร้างตัวแปรอินสแตนซ์แบบสำนวนนั้นอยู่ใน__init__วิธีการและไม่มีที่ไหนอีกแล้วในขณะที่คุณสามารถสร้างตัวแปรอินสแตนซ์ใหม่ในวิธีการอื่น ๆ หรือแม้กระทั่งในโค้ดที่ไม่เกี่ยวข้อง แต่ก็เป็นความคิดที่ไม่ดี มันจะทำให้รหัสของคุณยากที่จะหาเหตุผลหรือรักษา

ตัวอย่างเช่น:

class Thing(object):

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

ง่าย. ตอนนี้อินสแตนซ์ของคลาสนี้มีmagicแอตทริบิวต์:

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

การสร้างตัวแปรในเนมสเปซของคลาสนั้นนำไปสู่พฤติกรรมที่แตกต่างกันโดยสิ้นเชิง มันแตกต่างกันตามหน้าที่และคุณควรทำก็ต่อเมื่อคุณมีเหตุผลที่เฉพาะเจาะจง ตัวอย่างเช่น:

class Thing(object):

    magic = "Magic"

    def __init__(self):
        pass

ตอนนี้ลอง:

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

หรือ:

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"]

เนื่องจากเนมสเปซของคลาสนั้นแตกต่างจากเนมสเปซของอ็อบเจ็กต์ที่สร้างขึ้น ฉันจะปล่อยให้คุณค้นคว้าเพิ่มเติมอีกเล็กน้อย

ข้อความนำกลับบ้านคือ Python สำนวนคือการ (a) เริ่มต้นแอตทริบิวต์วัตถุใน__init__วิธีการของคุณและ (b) บันทึกพฤติกรรมของคลาสของคุณตามต้องการ คุณไม่จำเป็นต้องไปยุ่งยากกับเอกสารระดับสฟิงซ์ที่สมบูรณ์สำหรับทุกสิ่งที่คุณเคยเขียน แต่อย่างน้อยความคิดเห็นเกี่ยวกับรายละเอียดใด ๆ ที่คุณหรือคนอื่นอาจต้องหยิบมันขึ้นมา



1

ตัวแปรมีขอบเขตดังนั้นจึงเหมาะสมที่จะมีตัวแปรที่เฉพาะเจาะจงสำหรับฟังก์ชันของคุณ คุณไม่จำเป็นต้องชัดเจนเกี่ยวกับนิยามของคำเหล่านั้นเสมอไป โดยปกติคุณสามารถใช้มันได้ เฉพาะในกรณีที่คุณต้องการทำสิ่งที่เฉพาะเจาะจงกับประเภทของตัวแปรเช่นต่อท้ายรายการคุณจำเป็นต้องกำหนดก่อนที่จะเริ่มใช้งาน ตัวอย่างทั่วไปของสิ่งนี้

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

อย่างไรก็ตามนี่ไม่ใช่วิธีที่ดีในการตั้งค่ารายการ มันจะดีกว่าที่จะพูดว่า:

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

... แต่ฉันพูดนอกเรื่อง

คำถามอื่น ๆ ของคุณ ออบเจ็กต์ที่กำหนดเองควรเป็นคลาสเอง

class CustomObject(): # always capitalize the class name...this is not syntax, just style.
  pass
customObj = CustomObject()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.