Apakah Python sangat diketik?

234

Saya telah menemukan tautan yang mengatakan Python adalah bahasa yang sangat diketik.

Namun, saya pikir dalam bahasa yang sangat diketik Anda tidak bisa melakukan ini:

bob = 1
bob = "bob"

Saya pikir bahasa yang sangat diketik tidak menerima perubahan jenis pada saat dijalankan. Mungkin saya mendapatkan definisi yang salah (atau terlalu sederhana) tentang tipe kuat / lemah.

Jadi, apakah Python bahasa yang diketik dengan kuat atau lemah?

Pacane
sumber

Jawaban:

359

Python sangat, diketik secara dinamis.

  • Mengetik dengan kuat berarti jenis nilai tidak berubah dengan cara yang tidak terduga. String yang hanya berisi angka tidak secara ajaib menjadi angka, seperti yang mungkin terjadi pada Perl. Setiap perubahan jenis memerlukan konversi eksplisit.
  • Pengetikan dinamis berarti objek runtime (nilai) memiliki tipe, sebagai lawan dari pengetikan statis di mana variabel memiliki tipe.

Adapun contoh Anda

bob = 1
bob = "bob"

Ini berfungsi karena variabel tidak memiliki tipe; itu bisa menamai objek apa saja. Setelah bob=1, Anda akan menemukan itu type(bob)kembali int, tetapi setelah bob="bob"itu kembali str. (Perhatikan bahwa typeini adalah fungsi biasa, sehingga mengevaluasi argumennya, lalu mengembalikan jenis nilainya.)

Bandingkan ini dengan dialek C yang lebih lama, yang diketik dengan lemah, secara statis, sehingga pointer dan bilangan bulat cukup banyak dapat dipertukarkan. (ISO C modern membutuhkan konversi dalam banyak kasus, tetapi kompiler saya masih lunak tentang hal ini secara default.)

Saya harus menambahkan bahwa pengetikan kuat vs lemah lebih merupakan sebuah kontinum daripada pilihan boolean. C ++ memiliki pengetikan yang lebih kuat daripada C (dibutuhkan lebih banyak konversi), tetapi sistem tipe dapat ditumbangkan dengan menggunakan penunjuk pointer.

Kekuatan sistem tipe dalam bahasa yang dinamis seperti Python benar-benar ditentukan oleh bagaimana fungsi primitif dan pustaka merespons berbagai jenis. Misalnya, +kelebihan beban sehingga berfungsi pada dua angka atau dua string, tetapi bukan string dan angka. Ini adalah pilihan desain yang dibuat ketika +diimplementasikan, tetapi tidak benar-benar suatu keharusan mengikuti dari semantik bahasa. Faktanya, ketika Anda membebani +tipe khusus secara berlebihan , Anda dapat membuatnya secara implisit mengubah apa pun menjadi angka:

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)

Instance of class Foodapat ditambahkan ke objek lain:

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

Mengamati bahwa meskipun sangat diketik Python benar-benar baik-baik saja dengan menambahkan objek tipe intdan floatdan kembali objek tipe float(misalnya, int(42) + float(1)kembali 43.0). Di sisi lain, karena ketidakcocokan antara jenis Haskell akan mengeluh jika seseorang mencoba yang berikut ini (42 :: Integer) + (1 :: Float). Ini membuat Haskell bahasa yang diketik dengan ketat, di mana tipe sepenuhnya terpisah dan hanya bentuk overload yang terkontrol yang dimungkinkan melalui kelas tipe.

Fred Foo
sumber
18
Salah satu contoh yang tidak terlalu sering saya lihat tetapi saya pikir penting untuk menunjukkan bahwa Python tidak sepenuhnya diketik, adalah semua hal yang mengevaluasi ke boolean: docs.python.org/release/2.5.2/lib/truth.html
gsingh2011
25
Tidak begitu yakin jika ini adalah contoh balasan: Hal-hal dapat dievaluasi menjadi boolean, tetapi mereka tidak tiba-tiba "menjadi" boolean. Ini hampir seolah-olah seseorang secara implisit memanggil sesuatu seperti as_boolean (<value>), yang tidak sama dengan jenis objek itu sendiri yang berubah, bukan?
jbrendel
15
Menjadi jujur ​​dalam konteks boolean bukanlah contoh tandingan, karena tidak ada yang benar-benar dikonversi ke Trueatau False. Tapi bagaimana dengan promosi nomor? 1.0 + 2berfungsi dengan baik di Python seperti halnya di Perl atau C, meskipun "1.0" + 2tidak. Saya setuju dengan @jbrendel bahwa ini bukan konversi implisit, ini hanya overloading — tetapi dalam arti yang sama, Perl juga tidak melakukan konversi implisit. Jika fungsi tidak memiliki tipe parameter yang dideklarasikan, tidak ada tempat untuk konversi implisit terjadi.
abarnert
13
Cara yang lebih baik untuk berpikir tentang pengetikan yang kuat adalah tipe itu penting ketika melakukan operasi pada variabel. Jika jenisnya tidak seperti yang diharapkan, bahasa yang mengeluh diketik kuat (python / java) dan yang tidak diketik lemah (javascript) Bahasa yang diketik secara dinamis (python) adalah bahasa yang memungkinkan jenis variabel berubah di runtime sedangkan bahasa yang diketik secara statis (java) tidak memungkinkan ini setelah variabel dideklarasikan.
kashif
2
@ gsingh2011 Sejati berguna dan tidak lemah mengetik sendiri, tetapi tidak disengaja if isValid(value) - 1bisa bocor. Boolean dipaksa menjadi bilangan bulat, yang kemudian dievaluasi sebagai nilai kebenaran. False - 1menjadi benar dan True - 1menjadi salah, yang menyebabkan kesalahan dua-demi-satu yang memalukan sulit untuk di-debug. Dalam pengertian ini, kebanyakan python diketik dengan kuat; paksaan tipe biasanya tidak menyebabkan kesalahan logis.
Aaron3468
57

Ada beberapa masalah penting yang saya pikir tidak terjawab oleh semua jawaban yang ada.


Mengetik lemah berarti memungkinkan akses ke representasi yang mendasarinya. Di C, saya bisa membuat pointer ke karakter, kemudian memberi tahu kompiler saya ingin menggunakannya sebagai pointer ke integer:

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

Pada platform little-endian dengan bilangan bulat 32-bit, ini imenjadi array angka 0x64636261dan 0x00676665. Bahkan, Anda bahkan dapat mengarahkan pointer sendiri ke integer (dengan ukuran yang sesuai):

intptr_t i = (intptr_t)&sz;

Dan tentu saja ini berarti saya dapat menimpa memori di mana saja dalam sistem. *

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

* Tentu saja OS modern menggunakan memori virtual dan perlindungan halaman jadi saya hanya dapat menimpa memori proses saya sendiri, tetapi tidak ada apa pun tentang C itu sendiri yang menawarkan perlindungan seperti itu, seperti yang pernah dikodekan oleh siapa pun, katakanlah, Classic Mac OS atau Win16 dapat memberitahu Anda.

Lisp tradisional memungkinkan jenis peretasan yang serupa; pada beberapa platform, mengapung kata ganda dan sel kontra adalah tipe yang sama, dan Anda bisa meneruskan satu ke fungsi yang mengharapkan yang lain dan itu akan "bekerja".

Sebagian besar bahasa saat ini tidak cukup lemah seperti C dan Lisp, tetapi banyak dari mereka masih agak bocor. Misalnya, bahasa OO apa pun yang memiliki "downcast" yang tidak dicentang, * itu tipe kebocoran: Anda pada dasarnya memberi tahu kompiler "Saya tahu saya tidak memberi Anda informasi yang cukup untuk mengetahui ini aman, tapi saya cukup yakin itu adalah, "ketika seluruh titik dari sistem tipe adalah bahwa kompiler selalu memiliki informasi yang cukup untuk mengetahui apa yang aman.

* Downcast yang dicentang tidak membuat sistem tipe bahasa menjadi lebih lemah hanya karena ia menggerakkan check ke runtime. Jika ya, maka subtipe polimorfisme (alias panggilan fungsi virtual atau sepenuhnya dinamis) akan menjadi pelanggaran yang sama pada sistem tipe, dan saya tidak berpikir ada yang mau mengatakan itu.

Sangat sedikit bahasa "scripting" yang lemah dalam hal ini. Bahkan dalam Perl atau Tcl, Anda tidak dapat mengambil string dan hanya menafsirkan byte-nya sebagai integer. * Tetapi perlu dicatat bahwa dalam CPython (dan juga untuk banyak penerjemah lain untuk banyak bahasa), jika Anda benar-benar gigih, Anda dapat digunakan ctypesuntuk memuat libpython, membuang objek idke POINTER(Py_Object), dan memaksa sistem tipe bocor. Apakah ini membuat sistem tipe lemah atau tidak tergantung pada kasus penggunaan Anda — jika Anda mencoba menerapkan sandbox eksekusi terbatas dalam bahasa untuk memastikan keamanan, Anda harus berurusan dengan pelarian semacam ini ...

* Anda dapat menggunakan fungsi seperti struct.unpackmembaca byte dan membangun int baru dari "bagaimana C akan mewakili byte ini", tapi itu jelas tidak bocor; bahkan Haskell memungkinkan itu.


Sementara itu, konversi implisit benar-benar berbeda dari sistem tipe lemah atau bocor.

Setiap bahasa, bahkan Haskell, memiliki fungsi untuk, misalnya, mengubah integer menjadi string atau float. Tetapi beberapa bahasa akan melakukan beberapa konversi untuk Anda secara otomatis — misalnya, dalam C, jika Anda memanggil fungsi yang menginginkan float, dan Anda meneruskannya int, konversi akan dilakukan untuk Anda. Ini pasti dapat menyebabkan bug dengan, misalnya, luapan yang tidak terduga, tetapi mereka bukan jenis bug yang sama dengan yang Anda dapatkan dari sistem tipe lemah. Dan C tidak benar-benar menjadi lebih lemah di sini; Anda dapat menambahkan int dan float di Haskell, atau bahkan menggabungkan float ke string, Anda hanya perlu melakukannya secara lebih eksplisit.

Dan dengan bahasa yang dinamis, ini cukup suram. Tidak ada yang namanya "fungsi yang menginginkan pelampung" dengan Python atau Perl. Tetapi ada fungsi kelebihan beban yang melakukan hal-hal yang berbeda dengan tipe yang berbeda, dan ada perasaan intuitif yang kuat bahwa, misalnya, menambahkan string ke sesuatu yang lain adalah "fungsi yang menginginkan string". Dalam arti itu, Perl, Tcl, dan JavaScript tampaknya melakukan banyak konversi implisit ( "a" + 1memberi Anda "a1"), sementara Python melakukan lebih sedikit ( "a" + 1menimbulkan pengecualian, tetapi 1.0 + 1memberi Anda 2.0*). Sulit untuk memahami hal itu dalam istilah formal — mengapa tidak ada +yang mengambil string dan int, ketika jelas ada fungsi lain, seperti pengindeksan, yang bisa?

* Sebenarnya, dalam Python modern, itu dapat dijelaskan dalam hal subtyping OO, karena isinstance(2, numbers.Real)itu benar. Saya tidak berpikir ada arti di mana 2merupakan turunan dari tipe string di Perl atau JavaScript ... meskipun di Tcl, sebenarnya, karena semuanya adalah turunan dari string.


Akhirnya, ada definisi lain, sepenuhnya ortogonal, dari pengetikan "kuat" vs. "lemah", di mana "kuat" berarti kuat / fleksibel / ekspresif.

Misalnya, Haskell memungkinkan Anda menentukan tipe yang merupakan angka, string, daftar tipe ini, atau peta dari string ke tipe ini, yang merupakan cara sempurna untuk mewakili apa pun yang dapat diterjemahkan dari JSON. Tidak ada cara untuk mendefinisikan tipe seperti itu di Java. Tetapi setidaknya Java memiliki tipe parametrik (generik), jadi Anda dapat menulis fungsi yang mengambil Daftar T dan mengetahui bahwa elemen-elemennya adalah tipe T; bahasa lain, seperti Java awal, memaksa Anda untuk menggunakan Daftar Objek dan tertunduk. Tetapi setidaknya Java memungkinkan Anda membuat tipe baru dengan metode mereka sendiri; C hanya memungkinkan Anda membuat struktur. Dan BCPL bahkan tidak memilikinya. Dan seterusnya ke perakitan, di mana satu-satunya jenis panjang bit yang berbeda.

Jadi, dalam hal itu, sistem tipe Haskell lebih kuat dari Jawa modern, yang lebih kuat dari Jawa sebelumnya, yang lebih kuat dari C, yang lebih kuat dari BCPL.

Jadi, di mana Python cocok dengan spektrum itu? Itu agak rumit. Dalam banyak kasus, mengetik bebek memungkinkan Anda untuk mensimulasikan semua yang dapat Anda lakukan di Haskell, dan bahkan beberapa hal yang tidak dapat Anda lakukan; yakin, kesalahan ditangkap saat runtime alih-alih waktu kompilasi, tetapi masih tertangkap. Namun, ada beberapa kasus di mana mengetik bebek tidak cukup. Misalnya, di Haskell, Anda dapat mengatakan bahwa daftar int kosong adalah daftar int, sehingga Anda dapat memutuskan bahwa pengurangan +atas daftar tersebut harus mengembalikan 0 *; dalam Python, daftar kosong adalah daftar kosong; tidak ada jenis informasi untuk membantu Anda memutuskan pengurangan apa yang +harus dilakukan.

* Sebenarnya, Haskell tidak membiarkan Anda melakukan ini; jika Anda memanggil fungsi pengurangan yang tidak mengambil nilai awal pada daftar kosong, Anda mendapatkan kesalahan. Tetapi sistem tipenya cukup kuat sehingga Anda bisa membuatnya bekerja, dan Python tidak.

abarnert
sumber
3
Jawaban ini brilian! Sayang sekali sudah lama merana di bagian bawah daftar.
LeoR
1
Hanya sedikit komentar untuk contoh C Anda: char sz[]bukan pointer ke char, itu array char, dan dalam tugas itu meluruh menjadi pointer.
majkel.mk
39

Anda membingungkan 'sangat diketik' dengan 'diketik secara dinamis' .

Saya tidak bisa mengubah tipe 1dengan menambahkan string '12', tetapi saya bisa memilih tipe apa yang saya simpan dalam variabel dan mengubahnya selama waktu program dijalankan.

Kebalikan dari pengetikan dinamis adalah pengetikan statis; yang deklarasi jenis variabel tidak berubah selama masa program. Kebalikan dari pengetikan kuat adalah pengetikan lemah; jenis nilai dapat berubah selama masa program.

Martijn Pieters
sumber
Deskripsi di tautan sangat diketik: "Secara umum, bahasa yang diketik sangat memiliki aturan pengetikan yang lebih ketat pada waktu kompilasi, yang menyiratkan bahwa kesalahan dan pengecualian lebih mungkin terjadi selama kompilasi." menyiratkan Python adalah bahasa yang diketik dengan lemah ..., apakah wiki salah?
Hujan
1
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼: itu tidak tersirat sama sekali. Python memiliki aturan pengetikan yang ketat pada waktu kompilasi, setiap objek yang dibuat hanya memiliki satu jenis. Dan 'umumnya' tidak menyiratkan apa pun, itu hanya berarti bahwa Python adalah pengecualian untuk itu.
Martijn Pieters
24

Menurut artikel Python wiki ini Python diketik secara dinamis dan sangat kuat (memberikan penjelasan yang baik juga).

Mungkin Anda berpikir tentang bahasa yang diketik secara statis di mana jenis tidak dapat berubah selama pelaksanaan program dan pemeriksaan jenis terjadi selama waktu kompilasi untuk mendeteksi kemungkinan kesalahan.

Pertanyaan SO ini mungkin menarik: Bahasa bertipe dinamis versus bahasa bertipe statis dan artikel Wikipedia tentang Sistem Tipe ini memberikan informasi lebih lanjut

Levon
sumber
18

TLDR;

Pengetikan Python adalah Dinamis sehingga Anda dapat mengubah variabel string ke int

x = 'somestring'
x = 50

Mengetik Python adalah Kuat sehingga Anda tidak dapat menggabungkan tipe:

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

Dalam Javascript yang diketik dengan lemah ini terjadi ...

 'foo'+3 = 'foo3'

Mengenai Jenis Inferensi

Java memaksa Anda untuk secara eksplisit mendeklarasikan tipe objek Anda

int x = 50

Kotlin menggunakan inferensi untuk menyadari ituint

x = 50

Tetapi karena kedua bahasa menggunakan tipe statis , xtidak dapat diubah dari int. Bahasa tidak akan memungkinkan perubahan dinamis seperti

x = 50
x = 'now a string'
Adam Hughes
sumber
Saya tidak tahu detail Javascript tetapi 'x' + 3mungkin operator+kelebihan beban dan melakukan konversi tipe di belakang layar?
Hujan
3
Bagaimanapun, jawaban Anda sebenarnya lebih ringkas dan mudah dipahami daripada jawaban di atas.
Hujan
8

Sudah dijawab beberapa kali, tetapi Python adalah bahasa yang sangat diketik:

>>> 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'

Berikut ini dalam JavaScript:

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

Itulah perbedaan antara pengetikan lemah dan pengetikan kuat. Jenis yang lemah secara otomatis mencoba mengonversi dari satu jenis ke jenis lain, tergantung pada konteks (mis. Perl). Tipe yang kuat tidak pernah mengonversi secara implisit.

Kebingungan Anda terletak pada kesalahpahaman tentang bagaimana Python mengikat nilai ke nama (biasanya disebut sebagai variabel).

Dalam Python, nama tidak memiliki tipe, sehingga Anda dapat melakukan hal-hal seperti:

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

Dan nama dapat terikat pada apa pun:

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

Untuk bacaan lebih lanjut:

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

dan yang sedikit terkait tetapi lebih maju:

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

Wayne Werner
sumber
1
Beberapa tahun kemudian - sumber daya lain yang berguna dan relevan: youtu.be/_AEJHKGk9ns
Wayne Werner
Pengetikan kuat vs lemah tidak ada hubungannya dengan jenis hasil ekspresi seperti 3 + '4'. JavaScript sama kuatnya dengan Python untuk contoh ini.
qznc
@qznc bagaimana Javasript sama kuatnya? Saya tidak percaya saya tahu bahwa itu ada hubungannya dengan jenis yang dihasilkan, memang saya secara eksplisit menyatakan tipe Lemah secara otomatis mencoba untuk mengkonversi dari satu jenis ke yang lain .
Wayne Werner
2
@ oneloop itu belum tentu benar, hanya saja perilaku untuk menggabungkan float dan int didefinisikan dengan baik, dan menghasilkan float. Anda dapat melakukannya "3"*4dengan python juga. Hasilnya tentu saja, adalah "3333". Anda tidak akan mengatakan bahwa itu mengubah hal baik. Tentu saja itu bisa jadi hanya perdebatan semantik.
Wayne Werner
1
@ oneloop Ini tidak selalu benar bahwa karena Python menghasilkan floatdari kombinasi floatdan intbahwa itu mengkonversi tipe secara implisit. Ada hubungan alami antara float dan int, dan memang, jenis heirarki menjelaskannya. Saya kira Anda bisa berargumen mempertimbangkan Javascript '3'+4dan 'e'+4keduanya menjadi operasi yang terdefinisi dengan cara yang sama yang Python anggap 3.0 + 4didefinisikan dengan baik, tetapi pada saat itu maka benar-benar tidak ada yang namanya tipe kuat atau lemah, hanya (tidak) didefinisikan operasi.
Wayne Werner
6

Variabel Python menyimpan referensi yang tidak diketik ke objek target yang mewakili nilai.

Setiap operasi penugasan berarti menetapkan referensi yang tidak diketik ke objek yang ditugaskan - yaitu objek dibagi melalui referensi asli dan yang baru (dihitung).

Jenis nilai terikat ke objek target, bukan ke nilai referensi. Pemeriksaan tipe (kuat) dilakukan ketika operasi dengan nilai dilakukan (run time).

Dengan kata lain, variabel (secara teknis) tidak memiliki tipe - tidak masuk akal untuk berpikir dalam bentuk tipe variabel jika seseorang ingin tepat. Tetapi referensi secara otomatis direferensikan dan kita benar-benar berpikir dalam hal jenis objek target.

semangat
sumber
6

Istilah "pengetikan kuat" tidak memiliki definisi yang pasti.

Oleh karena itu, penggunaan istilah tergantung pada dengan siapa Anda berbicara.

Saya tidak mempertimbangkan bahasa apa pun, di mana jenis variabel tidak dinyatakan secara eksplisit, atau diketik secara statis untuk diketik dengan kuat.

Pengetikan yang kuat tidak hanya menghalangi konversi (misalnya, "secara otomatis" mengkonversi dari integer ke string). Itu menghalangi tugas (yaitu, mengubah jenis variabel).

Jika kode berikut dikompilasi (ditafsirkan), bahasa tidak diketik dengan kuat:

Foo = 1 Foo = "1"

Dalam bahasa yang sangat diketik, seorang programmer dapat "mengandalkan" suatu jenis.

Misalnya, jika seorang programmer melihat deklarasi tersebut,

UINT64 kZarkCount;

dan dia tahu bahwa 20 baris kemudian, kZarkCount masih merupakan UINT64 (selama itu terjadi di blok yang sama) - tanpa harus memeriksa kode intervensi.

pengguna5330045
sumber
1

Saya baru saja menemukan cara ringkas yang hebat untuk menghafalnya:

Pengetik yang dinamis / statis; nilai diketik sangat / lemah.

Hujan
sumber
0

saya pikir, contoh sederhana ini harus Anda jelaskan perbedaan antara pengetikan yang kuat dan dinamis:

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

Jawa:

public static void main(String[] args) {
        int i = 1;
        i = "1"; //will be error
        i = '0.1'; // will be error
    }
Dmitry Zagorulkin
sumber
Kode python Anda menunjukkan pengetikan dinamis sementara java menunjukkan pengetikan statis. Contoh yang lebih baik adalah $ var = '2' + 1 // hasilnya adalah 3
erichlf
@ivleph saya setuju. itu juga mungkin untuk menulis sesuatu seperti ini: "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....

Di atas akan menciptakan mimpi buruk dari kode yang tidak dapat dipertahankan dalam sistem besar selama jangka waktu yang lama. Sebut saja apa yang Anda inginkan, tetapi kemampuan untuk "secara dinamis" mengubah jenis variabel hanyalah ide yang buruk ...

Ryan Alexander
sumber