Python พิมพ์อย่างรุนแรงหรือไม่?


234

ฉันเจอลิงก์ที่บอกว่า Python เป็นภาษาที่พิมพ์ได้ดีมาก

อย่างไรก็ตามฉันคิดว่าในภาษาที่พิมพ์อย่างรุนแรงคุณไม่สามารถทำได้:

bob = 1
bob = "bob"

ฉันคิดว่าภาษาที่พิมพ์ออกมารุนแรงไม่ยอมรับการเปลี่ยนแปลงประเภทในเวลาทำงาน บางทีฉันอาจจะมีคำจำกัดความที่ผิด (หรือง่ายเกินไป) สำหรับประเภทที่แข็งแกร่ง / อ่อนแอ

ดังนั้น Python เป็นภาษาที่พิมพ์อย่างหนักหน่วงหรือไม่?

คำตอบ:


359

Python เป็นแบบไดนามิกที่แข็งแกร่ง

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

สำหรับตัวอย่างของคุณ

bob = 1
bob = "bob"

วิธีนี้ใช้งานได้เนื่องจากตัวแปรไม่มีประเภท มันสามารถตั้งชื่อวัตถุใด ๆ หลังจากที่bob=1คุณจะพบว่าtype(bob)ผลตอบแทนintแต่หลังจากนั้นก็ให้ผลตอบแทนbob="bob" str(โปรดทราบว่าtypeเป็นฟังก์ชั่นปกติดังนั้นจึงประเมินอาร์กิวเมนต์ของมันแล้วส่งกลับชนิดของค่า)

ตัดกันสิ่งนี้กับภาษาถิ่นเก่าของ C ซึ่งอ่อนกว่าพิมพ์แบบคงที่เพื่อให้พอยน์เตอร์และจำนวนเต็มแทนกันได้ค่อนข้างมาก (ISO C สมัยใหม่ต้องการการแปลงในหลายกรณี แต่คอมไพเลอร์ของฉันยังคงผ่อนปรนเรื่องนี้เป็นค่าเริ่มต้น)

ฉันต้องเพิ่มว่าการพิมพ์ที่รัดกุมและอ่อนแอนั้นมีความต่อเนื่องมากกว่าตัวเลือกแบบบูลีน C ++ มีการพิมพ์ที่แรงกว่า C (จำเป็นต้องมีการแปลงเพิ่ม) แต่ระบบประเภทนั้นสามารถล้มล้างได้โดยใช้พร็อพพอยน์เตอร์

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

def to_number(x):
    """Try to convert function argument to float-type object."""
    try: 
        return float(x) 
    except (TypeError, ValueError): 
        return 0 

class Foo:
    def __init__(self, number): 
        self.number = number

    def __add__(self, other):
        return self.number + to_number(other)

อินสแตนซ์ของคลาสFooสามารถเพิ่มไปยังวัตถุอื่น ๆ :

>>> a = Foo(42)
>>> a + "1"
43.0
>>> a + Foo
42
>>> a + 1
43.0
>>> a + None
42

สังเกตว่าพิมพ์แม้ว่าของูหลามจะสมบูรณ์ดีกับการเพิ่มวัตถุชนิดintและfloatและผลตอบแทนที่วัตถุของการพิมพ์float(เช่นint(42) + float(1)ผลตอบแทน43.0) บนมืออื่น ๆ เนื่องจากการไม่ตรงกันระหว่างประเภท Haskell (42 :: Integer) + (1 :: Float)จะบ่นถ้าใครพยายามต่อไป สิ่งนี้ทำให้ Haskell เป็นภาษาที่พิมพ์อย่างเคร่งครัดโดยที่ประเภทจะแยกออกจากกันอย่างสิ้นเชิงและมีเพียงรูปแบบการควบคุมการบรรทุกเกินพิกัดที่เป็นไปได้ผ่านคลาสประเภท


18
ตัวอย่างหนึ่งที่ฉันไม่ค่อยเห็นบ่อยนัก แต่ฉันคิดว่าสำคัญที่ต้องแสดงว่า Python ไม่ได้พิมพ์อย่างสมบูรณ์เป็นสิ่งที่ประเมินเป็นบูลีน: docs.python.org/release/2.5.2/lib/truth.html
gsingh2011

25
ไม่แน่ใจว่านี่เป็นตัวอย่างที่เคาน์เตอร์หรือเปล่า: สิ่งต่าง ๆ สามารถประเมินเป็นบูลีนได้ แต่พวกเขาก็ไม่ได้ "กลายเป็นบูลีน" ในทันที มันเกือบจะเหมือนกับว่ามีบางคนเรียกสิ่งที่คล้ายกันว่า as_boolean (<value>) ซึ่งไม่เหมือนกับชนิดของวัตถุที่เปลี่ยนแปลงตัวเองใช่ไหม
jbrendel

15
เป็น truthy ในบริบทแบบบูลไม่ได้เป็น counterexample เพราะไม่มีอะไรที่เป็นจริงถูกแปลงไปหรือTrue Falseแต่สิ่งที่เกี่ยวกับโปรโมชั่นจำนวน? 1.0 + 2ทำงานได้ดีใน Python เช่นเดียวกับใน Perl หรือ C แม้ว่า"1.0" + 2จะไม่ได้ ฉันเห็นด้วยกับ @jbrendel ว่านี่ไม่ใช่การแปลงโดยนัยจริงๆมันเป็นเพียงการโหลดมากเกินไป - แต่ในแง่เดียวกัน Perl ก็ไม่ได้ทำการแปลงโดยนัยใด ๆ หากฟังก์ชั่นไม่ได้ประกาศประเภทพารามิเตอร์จะไม่มีการแปลงเกิดขึ้นโดยปริยาย
abarnert

13
วิธีที่ดีกว่าที่จะคิดเกี่ยวกับการพิมพ์ที่แข็งแกร่งคือประเภทนั้นมีความสำคัญเมื่อดำเนินการกับตัวแปร หากประเภทไม่เป็นไปตามที่คาดไว้ภาษาที่มีการพิมพ์จะรุนแรงมาก (python / java) และภาษาที่ไม่ได้พิมพ์อย่างอ่อน (javascript) ภาษาที่พิมพ์แบบไดนามิก (python) เป็นภาษาที่อนุญาตให้ชนิดของตัวแปรเปลี่ยนที่ รันไทม์ในขณะที่ภาษาที่พิมพ์แบบคงที่ (java) ไม่อนุญาตสิ่งนี้เมื่อมีการประกาศตัวแปร
kashif

2
@ gsingh2011 ความจริงมีประโยชน์และไม่พิมพ์อ่อนแอด้วยตัวเอง แต่อุบัติเหตุif isValid(value) - 1อาจรั่วไหลได้ บูลีนถูกบีบอัดให้เป็นจำนวนเต็มซึ่งจะถูกประเมินเป็นค่าจริง False - 1กลายเป็นความจริงและTrue - 1กลายเป็นเท็จนำไปสู่ข้อผิดพลาดสองชั้นที่น่าอับอายที่จะแก้ไขข้อผิดพลาดแบบสองชั้น ในกรณีนี้งูหลามส่วนใหญ่จะพิมพ์อย่างรุนแรง; ประเภทการข่มขู่มักจะไม่ทำให้เกิดข้อผิดพลาดเชิงตรรกะ
Aaron3468

57

มีบางประเด็นที่สำคัญที่ฉันคิดว่าคำตอบที่มีอยู่ทั้งหมดพลาดไป


การพิมพ์ที่อ่อนแอหมายถึงการอนุญาตให้เข้าถึงการเป็นตัวแทนพื้นฐาน ใน C ฉันสามารถสร้างตัวชี้ไปยังตัวละครแล้วบอกคอมไพเลอร์ฉันต้องการใช้มันเป็นตัวชี้ไปยังจำนวนเต็ม:

char sz[] = "abcdefg";
int *i = (int *)sz;

บนแพลตฟอร์มน้อย endian กับจำนวนเต็ม 32 บิตนี้จะทำให้iเป็น array ของตัวเลขที่และ0x64636261 0x00676665ในความเป็นจริงคุณสามารถทอดตัวชี้ไปที่จำนวนเต็ม (ขนาดที่เหมาะสม):

intptr_t i = (intptr_t)&sz;

และแน่นอนนี่หมายความว่าฉันสามารถเขียนทับหน่วยความจำได้ทุกที่ในระบบ *

char *spam = (char *)0x12345678
spam[0] = 0;

* แน่นอนว่าระบบปฏิบัติการสมัยใหม่ใช้หน่วยความจำเสมือนและการป้องกันหน้าดังนั้นฉันจึงสามารถเขียนทับหน่วยความจำของกระบวนการของฉันเองได้ แต่ไม่มีอะไรเกี่ยวกับตัว C ที่ให้การป้องกันเช่นเดียวกับใครก็ตามที่เคยเขียนโค้ด Classic Mac OS หรือ Win16 สามารถบอกคุณได้

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

ภาษาส่วนใหญ่ทุกวันนี้ยังไม่อ่อนแอเท่า C และ Lisp แต่หลาย ๆ คนก็ยังรั่วอยู่ ตัวอย่างเช่นภาษา OO ใด ๆ ที่มี "downcast" ที่ไม่ถูกตรวจสอบ * เป็นประเภทที่มีการรั่วไหล: คุณกำลังบอกคอมไพเลอร์ว่า "ฉันรู้ว่าฉันไม่ได้ให้ข้อมูลเพียงพอที่จะรู้ว่าสิ่งนี้ปลอดภัย แต่ฉันค่อนข้างแน่ใจ มันคือ "เมื่อจุดรวมของระบบประเภทนั้นคอมไพเลอร์มีข้อมูลเพียงพอเสมอที่จะรู้ว่าอะไรปลอดภัย

* การดาวน์สตรีมที่ถูกตรวจสอบไม่ได้ทำให้ระบบประเภทของภาษาอ่อนแอลงเพียงเพราะมันย้ายการตรวจสอบไปยังรันไทม์ ถ้าเป็นเช่นนั้นแล้วความหลากหลายย่อย (เรียกว่าการเรียกใช้ฟังก์ชั่นเสมือนหรือแบบไดนามิกเต็มรูปแบบ) จะเป็นการละเมิดระบบประเภทเดียวกันและฉันไม่คิดว่ามีใครต้องการพูดแบบนั้น

ภาษา "สคริปต์" น้อยมากที่อ่อนแอในแง่นี้ แม้ใน Perl หรือ Tcl คุณไม่สามารถใช้สตริงและเพียงตีความไบต์เป็นจำนวนเต็ม * แต่มันก็คุ้มค่าที่จะสังเกตว่าใน CPython (และในทำนองเดียวกันสำหรับล่ามภาษาอื่น ๆ ในหลายภาษา) ถ้าคุณอดทนจริง ๆ คุณ สามารถใช้ctypesในการโหลดยกlibpythonวัตถุidไปยังPOINTER(Py_Object)และบังคับให้ระบบประเภทการรั่วไหล ไม่ว่าสิ่งนี้จะทำให้ระบบประเภทอ่อนแอหรือไม่ขึ้นอยู่กับกรณีการใช้งานของคุณ - หากคุณกำลังพยายามใช้ Sandbox การดำเนินการที่ จำกัด ในภาษาเพื่อความปลอดภัยคุณต้องจัดการกับการหลบหนีเหล่านี้ ...

* คุณสามารถใช้ฟังก์ชั่นที่ต้องการstruct.unpackอ่านไบต์และสร้าง int ใหม่จาก "วิธี C จะแสดงถึงไบต์เหล่านี้" แต่เห็นได้ชัดว่าไม่รั่วไหล; แม้ Haskell ก็ยอมให้


ในขณะเดียวกันการแปลงโดยนัยเป็นสิ่งที่แตกต่างจากระบบแบบอ่อนแอหรือรั่ว

ทุกภาษาแม้แต่ Haskell มีฟังก์ชั่นพูดแปลงค่าจำนวนเต็มเป็นสตริงหรือทศนิยม แต่ภาษาบางภาษาจะทำการแปลงให้คุณโดยอัตโนมัติเช่นใน C หากคุณเรียกใช้ฟังก์ชันที่ต้องการfloatและคุณผ่านมันintไประบบจะแปลงให้คุณ สิ่งนี้สามารถนำไปสู่ข้อผิดพลาดเช่นล้นมากเกินคาด แต่พวกมันไม่ใช่บั๊กแบบเดียวกับที่คุณได้รับจากระบบที่อ่อนแอ และ C ไม่ได้อ่อนแอลงที่นี่จริงๆ คุณสามารถเพิ่ม int และการลอยใน Haskell หรือแม้กระทั่งการเชื่อมต่อการลอยกับสตริงคุณเพียงแค่ต้องทำอย่างชัดเจนมากขึ้น

และด้วยภาษาแบบไดนามิกนี่เป็นสิ่งที่ค่อนข้างมืดมน ไม่มีสิ่งเช่น "ฟังก์ชั่นที่ต้องการลอย" ใน Python หรือ Perl แต่มีฟังก์ชั่นโอเวอร์โหลดที่ทำสิ่งที่แตกต่างกับประเภทที่แตกต่างกันและมีความรู้สึกที่ใช้งานง่ายเช่นการเพิ่มสตริงในสิ่งอื่นคือ "ฟังก์ชั่นที่ต้องการสตริง" ในแง่นั้น Perl, Tcl และ JavaScript ดูเหมือนจะทำการแปลงโดยนัย ( "a" + 1ให้คุณ"a1") จำนวนมากในขณะที่ Python ทำน้อยกว่ามาก ( "a" + 1ยกข้อยกเว้น แต่1.0 + 1ให้คุณ2.0*) เป็นเรื่องยากที่จะนำความรู้สึกนั้นมาใช้ในรูปแบบทางการ - ทำไมไม่ควร+ที่จะมีสตริงและ int เมื่อมีฟังก์ชั่นอื่น ๆ เช่นการจัดทำดัชนีอย่างชัดเจน

* ที่จริงแล้วใน Python สมัยใหม่นั้นสามารถอธิบายได้ในแง่ของการพิมพ์ย่อย OO เนื่องจากisinstance(2, numbers.Real)เป็นจริง ฉันไม่คิดว่ามีความรู้สึกใดที่2เป็นตัวอย่างของประเภทสตริงใน Perl หรือ JavaScript ... แม้ว่าใน Tcl มันเป็นจริงเพราะทุกอย่างเป็นตัวอย่างของสตริง


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

ตัวอย่างเช่น Haskell ช่วยให้คุณกำหนดประเภทที่เป็นตัวเลขสตริงรายการประเภทนี้หรือแผนที่จากสตริงเป็นประเภทนี้ซึ่งเป็นวิธีที่สมบูรณ์แบบในการแสดงสิ่งที่สามารถถอดรหัสจาก JSON ไม่มีวิธีกำหนดประเภทดังกล่าวใน Java แต่อย่างน้อย Java มีประเภทพารามิเตอร์ (ทั่วไป) ดังนั้นคุณสามารถเขียนฟังก์ชั่นที่ใช้ List of T และรู้ว่าองค์ประกอบนั้นเป็นประเภท T; ภาษาอื่น ๆ เช่น Java รุ่นก่อนบังคับให้คุณใช้ List of Object และ downcast แต่อย่างน้อย Java ช่วยให้คุณสร้างประเภทใหม่ด้วยวิธีการของตนเอง C ช่วยให้คุณสร้างโครงสร้างเท่านั้น และ BCPL ไม่ได้มีสิ่งนั้น และต่อไปจนถึงแอสเซมบลีที่ชนิดเท่านั้นมีความยาวบิตที่แตกต่างกัน

ดังนั้นในแง่นี้ระบบประเภทของ Haskell จึงแข็งแกร่งกว่า Java รุ่นใหม่ซึ่งแข็งแกร่งกว่า Java รุ่นก่อน ๆ ซึ่งแข็งแกร่งกว่า C ซึ่งแข็งแกร่งกว่า BCPL

Python จะพอดีกับสเปกตรัมนั้นอยู่ที่ไหน? นั่นเป็นเรื่องยุ่งยากเล็กน้อย ในหลายกรณีการพิมพ์เป็ดช่วยให้คุณจำลองทุกสิ่งที่คุณสามารถทำได้ใน Haskell และแม้แต่บางสิ่งที่คุณทำไม่ได้ ตรวจสอบว่ามีข้อผิดพลาดเกิดขึ้นขณะทำงานแทนที่จะรวบรวมเวลา แต่ก็ยังคงติดอยู่ อย่างไรก็ตามมีหลายกรณีที่การพิมพ์เป็ดไม่เพียงพอ ตัวอย่างเช่นใน Haskell คุณสามารถบอกได้ว่ารายการว่างของ ints เป็นรายการของ ints ดังนั้นคุณสามารถตัดสินใจได้ว่าการลดลงของ+รายการนั้นควรส่งคืน 0 *; ใน Python รายการที่ว่างเปล่าเป็นรายการที่ว่างเปล่า ไม่มีข้อมูลประเภทที่จะช่วยให้คุณตัดสินใจได้ว่า+ควรลดขนาดใดลง

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


3
คำตอบนี้ยอดเยี่ยม! ความอัปยศที่มันอ่อนล้ามานานที่ด้านล่างของรายการ
LeoR

1
เพียงแค่ความคิดเห็นเล็ก ๆ น้อย ๆ กับตัวอย่าง C ของคุณ: char sz[]ไม่ใช่ตัวชี้ไปยัง char มันเป็นอาร์เรย์ของถ่านและในการกำหนดมันจะสลายตัวเป็นตัวชี้
majkel.mk

39

คุณกำลังสับสน'พิมพ์อย่างยิ่ง'กับ'แบบไดนามิกพิมพ์'

ฉันไม่สามารถเปลี่ยนประเภทของ 1โดยการเพิ่มสตริง'12'แต่ฉันสามารถเลือกประเภทที่ฉันเก็บไว้ในตัวแปรและเปลี่ยนในช่วงเวลาทำงานของโปรแกรม

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


คำอธิบายในลิงก์พิมพ์อย่างยิ่ง: "โดยทั่วไปภาษาที่พิมพ์อย่างรุนแรงมีกฎการพิมพ์ที่เข้มงวดในเวลารวบรวมซึ่งหมายความว่าข้อผิดพลาดและข้อยกเว้นมีแนวโน้มที่จะเกิดขึ้นระหว่างการรวบรวม" แปลว่า Python เป็นภาษาที่พิมพ์อย่างอ่อน ... , วิกินั้นผิดหรือเปล่า?
ฝนตก

1
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼: นั่นไม่ได้บอกเป็นนัยเลย Python มีกฎการพิมพ์ที่เข้มงวดในเวลารวบรวมแต่ละวัตถุที่สร้างขึ้นมีเพียงประเภทเดียว และ 'โดยทั่วไป' ไม่ได้บอกเป็นนัยเลยมันก็หมายความว่า Python เป็นข้อยกเว้น
Martijn Pieters

24

อ้างอิงจากวิกิ Python นี้บทความบทความ Python นั้นมีทั้งแบบไดนามิกและพิมพ์อย่างรุนแรง (ให้คำอธิบายที่ดีเช่นกัน)

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

คำถาม SO นี้อาจเป็นที่สนใจ: ภาษาประเภทไดนามิกกับภาษาประเภทคงที่และบทความ Wikipedia เกี่ยวกับType Systemsให้ข้อมูลเพิ่มเติม


18

TLDR;

การพิมพ์ของ Python เป็นแบบไดนามิกเพื่อให้คุณสามารถเปลี่ยนตัวแปรสตริงเป็น int

x = 'somestring'
x = 50

การพิมพ์ของ Python นั้นแข็งแกร่งดังนั้นคุณจึงไม่สามารถรวมประเภทต่างๆได้:

'foo' + 3 --> TypeError: cannot concatenate 'str' and 'int' objects

ในจาวาสคริปต์ที่พิมพ์ไม่ถึงจุดนี้จะเกิดขึ้น ...

 'foo'+3 = 'foo3'

เกี่ยวกับการอนุมานประเภท

Java บังคับให้คุณประกาศประเภทวัตถุของคุณอย่างชัดเจน

int x = 50

Kotlinใช้การอนุมานเพื่อให้ทราบว่าเป็นint

x = 50

แต่เนื่องจากทั้งสองภาษาใช้ไฟฟ้าสถิตชนิดไม่สามารถเปลี่ยนแปลงได้จากx intภาษาทั้งสองจะไม่อนุญาตให้มีการเปลี่ยนแปลงแบบไดนามิกเช่น

x = 50
x = 'now a string'

ฉันไม่ทราบรายละเอียดของ Javascript แต่'x' + 3อาจมีการoperator+ใช้งานมากเกินไปและทำการแปลงชนิดเบื้องหลัง
ฝนตก

3
อย่างไรก็ตามคำตอบของคุณนั้นกระชับและเข้าใจง่ายกว่าคำตอบข้างต้น
ฝนตก

8

มีการตอบไปแล้วสองสามครั้ง แต่ Python เป็นภาษาที่พิมพ์ออกมาอย่างมาก:

>>> x = 3
>>> y = '4'
>>> print(x+y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

ต่อไปนี้ใน JavaScript:

var x = 3    
var y = '4'
alert(x + y) //Produces "34"

นั่นคือความแตกต่างระหว่างการพิมพ์ที่อ่อนแอและการพิมพ์ที่แข็งแกร่ง ประเภทที่อ่อนแอจะพยายามแปลงจากประเภทหนึ่งเป็นประเภทอื่นโดยอัตโนมัติโดยขึ้นอยู่กับบริบท (เช่น Perl) ประเภทที่แข็งแกร่งไม่เคยแปลงโดยนัย

ความสับสนของคุณอยู่ที่การเข้าใจผิดว่า Python ผูกค่ากับชื่ออย่างไร (โดยทั่วไปเรียกว่าตัวแปร)

ใน Python ชื่อไม่มีประเภทดังนั้นคุณสามารถทำสิ่งต่าง ๆ เช่น:

bob = 1
bob = "bob"
bob = "An Ex-Parrot!"

และชื่อสามารถผูกกับอะไรก็ได้:

>>> def spam():
...     print("Spam, spam, spam, spam")
...
>>> spam_on_eggs = spam
>>> spam_on_eggs()
Spam, spam, spam, spam

สำหรับการอ่านเพิ่มเติม:

https://en.wikipedia.org/wiki/Dynamic_dispatch

และที่เกี่ยวข้องเล็กน้อย แต่สูงกว่า:

http://effbot.org/zone/call-by-object.htm


1
หลายปีต่อมา - อีกแหล่งข้อมูลที่มีประโยชน์และเกี่ยวข้อง: youtu.be/_AEJHKGk9ns
Wayne Werner

การพิมพ์ที่รัดกุมและอ่อนแอไม่เกี่ยวข้องกับประเภทผลลัพธ์ของนิพจน์เช่น 3 + '4' JavaScript มีความแข็งแกร่งเท่ากับ Python สำหรับตัวอย่างนี้
qznc

@qznc Javasript แข็งแกร่งแค่ไหน? ผมไม่เชื่อว่าผมคู่คิดว่ามันมีอะไรจะทำอย่างไรกับประเภทที่เกิดขึ้นจริงผมอย่างชัดเจนชนิดที่อ่อนแอลองโดยอัตโนมัติเพื่อการแปลงจากประเภทหนึ่งไปยังอีก
Wayne Werner

2
@oneloop ที่ไม่จำเป็นต้องเป็นจริงมันเป็นเพียงพฤติกรรมที่รวมกันของโฟลทและ ints ที่กำหนดไว้อย่างดีและผลในการลอย คุณสามารถทำ"3"*4ในหลามได้เช่นกัน "3333"ผลของการเรียนการสอนคือ คุณจะไม่พูดว่ามันแปลงสิ่งใดสิ่งหนึ่ง แน่นอนว่าอาจเป็นการโต้แย้งความหมาย
Wayne Werner

1
@oneloop มันไม่จำเป็นต้องเป็นความจริงเพราะ Python สร้างfloatจากการรวมกันของมันfloatและintมันเป็นการแปลงชนิดโดยปริยาย มีความสัมพันธ์ตามธรรมชาติระหว่างโฟลตและอินและแน่นอนว่าทายาทประเภทนี้จะสะกดออก ฉันคิดว่าคุณสามารถโต้แย้ง Javascript ที่พิจารณา'3'+4และ'e'+4ทั้งสองเป็นการดำเนินการที่กำหนดไว้อย่างดีในแบบเดียวกับที่ Python คิดว่า3.0 + 4จะถูกกำหนดไว้อย่างดี แต่ ณ จุดนั้นก็ไม่มีประเภทที่แข็งแกร่งหรืออ่อนแออย่างที่กำหนดไว้ (un) การดำเนินงาน
Wayne Werner

6

ตัวแปร Python เก็บการอ้างอิงแบบไม่พิมพ์ไปยังวัตถุเป้าหมายที่แสดงค่า

การดำเนินการใด ๆ ที่ได้รับมอบหมายหมายถึงการมอบหมายการอ้างอิงที่ไม่ได้พิมพ์ไปยังวัตถุที่กำหนด - นั่นคือการแบ่งปันวัตถุผ่านทางการอ้างอิงดั้งเดิมและการอ้างอิงใหม่ (นับ)

ชนิดค่าถูกผูกไว้กับวัตถุเป้าหมายไม่ใช่ค่าอ้างอิง การตรวจสอบประเภท (รัดกุม) จะกระทำเมื่อการดำเนินการกับค่าถูกดำเนินการ (เวลารัน)

กล่าวอีกนัยหนึ่งตัวแปร (ในทางเทคนิค) ไม่มีประเภท - มันไม่สมเหตุสมผลที่จะคิดในแง่ของประเภทตัวแปรหากใครต้องการแน่นอน แต่การอ้างอิงจะถูกยกเลิกการลงทะเบียนโดยอัตโนมัติและเราคิดในแง่ของประเภทของวัตถุเป้าหมาย


6

คำว่า "การพิมพ์ที่รัดกุม" ไม่มีคำจำกัดความที่แน่นอน

ดังนั้นการใช้คำขึ้นอยู่กับคนที่คุณพูด

ฉันไม่พิจารณาภาษาใด ๆ ซึ่งชนิดของตัวแปรไม่ได้ถูกประกาศอย่างชัดเจนหรือพิมพ์แบบสแตติกเพื่อพิมพ์อย่างยิ่ง

การพิมพ์ที่รัดกุมไม่เพียง แต่ขัดขวางการแปลง (ตัวอย่างเช่น "อัตโนมัติ" การแปลงจากจำนวนเต็มเป็นสตริง) มันห้ามการมอบหมาย (เช่นการเปลี่ยนประเภทของตัวแปร)

หากรหัสต่อไปนี้รวบรวม (การตีความ) ภาษาจะไม่ถูกพิมพ์อย่างหนัก:

Foo = 1 Foo = "1"

ในภาษาที่พิมพ์ออกมาอย่างแรงโปรแกรมเมอร์สามารถ "เชื่อใจได้" ประเภทหนึ่ง

ตัวอย่างเช่นหากโปรแกรมเมอร์เห็นการประกาศ

UINT64 kZarkCount;

และเขาหรือเธอรู้ว่า 20 บรรทัดในภายหลัง kZarkCount ยังคงเป็น UINT64 (ตราบใดที่มันเกิดขึ้นในบล็อกเดียวกัน) - โดยไม่ต้องตรวจสอบรหัสการแทรกแซง


1

ฉันเพิ่งค้นพบวิธีรัดกุมที่ยอดเยี่ยมในการจดจำ:

การขยายพิมพ์แบบไดนามิก / คงที่; ค่าที่พิมพ์อย่างรุนแรง / อ่อน


0

ฉันคิดว่าตัวอย่างง่ายๆนี้คุณควรอธิบายความแตกต่างระหว่างการพิมพ์ที่แข็งแกร่งและแบบไดนามิก:

>>> tup = ('1', 1, .1)
>>> for item in tup:
...     type(item)
...
<type 'str'>
<type 'int'>
<type 'float'>
>>>

Java:

public static void main(String[] args) {
        int i = 1;
        i = "1"; //will be error
        i = '0.1'; // will be error
    }

รหัสหลามของคุณแสดงให้เห็นถึงการพิมพ์แบบไดนามิกในขณะที่จาวาสาธิตการพิมพ์แบบคงที่ ตัวอย่างที่ดีกว่าคือ $ var = '2' + 1 // ผลลัพธ์คือ 3
erichlf

@ivleph ฉันเห็นด้วย เป็นไปได้ที่จะเขียนบางสิ่งเช่นนี้: "a" * 3 == "aaa"
Dmitry Zagorulkin

-4
class testme(object):
    ''' A test object '''
    def __init__(self):
        self.y = 0

def f(aTestMe1, aTestMe2):
    return aTestMe1.y + aTestMe2.y




c = testme            #get a variable to the class
c.x = 10              #add an attribute x inital value 10
c.y = 4               #change the default attribute value of y to 4

t = testme()          # declare t to be an instance object of testme
r = testme()          # declare r to be an instance object of testme

t.y = 6               # set t.y to a number
r.y = 7               # set r.y to a number

print(f(r,t))         # call function designed to operate on testme objects

r.y = "I am r.y"      # redefine r.y to be a string

print(f(r,t))         #POW!!!!  not good....

ข้างต้นจะสร้างฝันร้ายของรหัสที่ไม่สามารถรักษาได้ในระบบขนาดใหญ่ในช่วงเวลานาน เรียกมันว่าสิ่งที่คุณต้องการ แต่ความสามารถในการ "เปลี่ยน" แบบไดนามิกตัวแปรเป็นเพียงความคิดที่ไม่ดี ...

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.