Python güçlü yazılıyor mu?


234

Python'un güçlü bir dille yazılmış olduğunu söyleyen bağlantılarla karşılaştım.

Ancak, güçlü yazılan dillerde bunu yapamayacağınızı düşündüm:

bob = 1
bob = "bob"

Güçlü yazılan bir dilin çalışma zamanında tür değiştirmeyi kabul etmediğini düşündüm. Belki güçlü / zayıf türlerin yanlış (veya çok basit) bir tanımına sahibim.

Peki, Python güçlü veya zayıf yazılmış bir dil mi?

Yanıtlar:


359

Python güçlü, dinamik olarak yazılmıştır.

  • Güçlü yazma, bir değerin türünün beklenmedik şekillerde değişmeyeceği anlamına gelir. Yalnızca rakam içeren bir dize, Perl'de olduğu gibi sihirli bir şekilde sayı değildir. Her tür değişikliği için açık bir dönüşüm gerekir.
  • Dinamik yazma, çalışma zamanı nesnelerinin (değerlerinin), değişkenlerin bir türe sahip olduğu statik yazmanın aksine bir türe sahip olduğu anlamına gelir.

Örneğinize gelince

bob = 1
bob = "bob"

Değişkenin bir türü olmadığı için çalışır; herhangi bir nesneyi adlandırabilir. Sonra bob=1, bunun type(bob)geri geldiğini göreceksiniz int, ama sonrabob="bob" geri döner str. ( typeDüzenli bir işlev olduğunu unutmayın , bu nedenle bağımsız değişkenini değerlendirir, ardından değerin türünü döndürür.)

Bunu, zayıf, statik olarak yazılan eski C lehçeleriyle karşılaştırın, böylece işaretçiler ve tamsayılar hemen hemen değiştirilebilir. (Modern ISO C birçok durumda dönüşüm gerektirir, ancak derleyicim varsayılan olarak bu konuda hala yumuşaktır.)

Şunu da eklemeliyim ki güçlü ve zayıf yazım, bir boole seçiminden çok bir sürekliliktir. C ++, C'den daha güçlü yazım özelliğine sahiptir (daha fazla dönüşüm gerekir), ancak tür sistemi işaretçi dökümleri kullanılarak alt üst edilebilir.

Python gibi dinamik bir dilde yazı sisteminin gücü, ilkellerinin ve kütüphane işlevlerinin farklı türlere nasıl tepki verdiğiyle gerçekten belirlenir. Örneğin, +iki sayı veya iki dize üzerinde çalışacak , ancak bir dize ve bir sayı üzerinde çalışmayacak şekilde aşırı yüklenmiştir . Bu, +uygulandığında yapılan bir tasarım seçimidir , ancak dilin anlambiliminden sonra bir zorunluluk değildir. Aslında, +özel bir türe aşırı yüklediğinizde , herhangi bir şeyi dolaylı olarak bir sayıya dönüştürebilirsiniz:

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)

Sınıf örneği Foobaşka nesnelere eklenebilir:

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

Olsa bile kesinlikle yazılı Python tür nesneler ekleyerek tamamen iyi olduğunu gözlemleyin intve floatve getiri türünde bir nesne float(örneğin, int(42) + float(1)döner 43.0). Öte yandan, Haskell türleri arasındaki uyumsuzluk nedeniyle, aşağıdakileri denerse şikayetçi olur (42 :: Integer) + (1 :: Float). Bu, Haskell'i türlerin tamamen ayrıldığı ve tür sınıfları aracılığıyla yalnızca kontrollü bir aşırı yükleme biçiminin mümkün olduğu, kesinlikle yazılan bir dil haline getirir.


18
Çok sık göremediğim bir örnek ama Python'un tamamen güçlü bir şekilde yazılmadığını
gsingh2011

25
Bunun karşı bir örnek olup olmadığından emin değilim: İşler bir boole için değerlendirilebilir, ancak aniden bir boole haline gelmezler. Neredeyse biri, örtük olarak as_boolean (<value>) gibi bir şey çağırdı, bu da nesnenin kendisinin türüyle aynı değil, değil mi?
jbrendel

15
Boolean bağlamında doğruluk bir karşı örnek değildir, çünkü hiçbir şey aslında Trueveya biçimine dönüştürülmez False. Peki ya sayı tanıtımı? 1.0 + 2Python'da Perl veya C'de olduğu gibi çalışıyor "1.0" + 2. @Jbrendel ile bunun örtük bir dönüşüm olmadığını, sadece aşırı yüklendiğini kabul ediyorum - ama aynı anlamda Perl de örtük bir dönüşüm yapmıyor. İşlevler parametre türlerini bildirmediyse, örtük dönüştürmelerin gerçekleşeceği hiçbir yer yoktur.
abarnert

13
Güçlü yazmayı düşünmenin daha iyi bir yolu , bir değişken üzerinde işlem yaparken türün önemli olmasıdır. Tür beklendiği gibi değilse, şikayet eden bir dil güçlü bir şekilde yazılır (python / java) ve zayıf yazılmamış bir dil ( javascript) Dinamik olarak yazılan diller (python), bir değişkenin türünün değiştirilmesine izin veren dillerdir oysa çalışma zamanı statik olarak yazılan bir değişkeni bildirildi kez dilleri (java) bu izin vermez.
kashif

2
@ gsingh2011 Doğruluk yararlıdır ve kendi başına zayıf yazmaz , ancak kazara if isValid(value) - 1sızabilir. Boolean tamsayıya zorlanır ve bu daha sonra gerçek bir değer olarak değerlendirilir. False - 1gerçeğe dönüşür ve sahte True - 1olur, hata ayıklamak için utanç verici derecede zor iki katmanlı birer birer hataya yol açar. Bu anlamda, python çoğunlukla kuvvetle yazılmıştır; tip zorlamalar genellikle mantıksal hatalara neden olmaz.
Aaron3468

57

Mevcut yanıtların tamamının kaçırdığını düşündüğüm bazı önemli konular var.


Zayıf yazma, temeldeki temsile erişimin sağlanması anlamına gelir. C de, karakterlere bir işaretçi oluşturabilir, sonra derleyiciye tamsayılara işaretçi olarak kullanmak istediğimi söyleyebilirim:

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

32 bit tamsayıları olan küçük bir endian platformunda, bu isayı 0x64636261ve bir dizi haline getirir 0x00676665. Aslında, işaretçileri bile tam sayılara (uygun boyutta) atabilirsiniz:

intptr_t i = (intptr_t)&sz;

Ve elbette bu, sistemin herhangi bir yerinde belleğin üzerine yazabileceğim anlamına geliyor. *

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

* Elbette modern işletim sistemleri sanal belleği ve sayfa korumasını kullanıyor, bu yüzden sadece kendi işlemimin belleğinin üzerine yazabiliyorum, ancak C'nin kendisi hakkında kodlanmış olan herkesin söyleyebileceği gibi böyle bir koruma sunan hiçbir şey yok.

Geleneksel Lisp benzer tür bir hackery izin verdi; bazı platformlarda, çift kelimelik yüzer ve eksileri aynı türdeydi ve birini diğerini bekleyen bir işleve geçirebilirdiniz ve "işe yarar".

Bugün çoğu dil C ve Lisp kadar zayıf değil, ancak birçoğu hala biraz sızdı. Örneğin, denetlenmeyen bir "mahzun" olan herhangi bir OO dili, * bu bir tür sızıntıdır: temel olarak derleyiciye "Güvenli olduğunu bilmeniz için yeterli bilgi vermediğimi biliyorum, ama eminim "bir yazım sisteminin asıl amacı derleyicinin neyin güvenli olduğunu bilmek için yeterli bilgiye sahip olmasıdır.

* İşaretli bir downcast, dili çalışma zamanına taşıdığı için dilin tür sistemini daha zayıf yapmaz. Eğer öyleyse, alt tür polimorfizmi (sanal veya tam dinamik fonksiyon çağrıları olarak da bilinir), tip sisteminin aynı ihlali olurdu ve kimsenin bunu söylemek istediğini sanmıyorum.

Çok az sayıda "betik" dili bu anlamda zayıftır. Perl veya Tcl'de bile, bir dize alıp baytlarını bir tamsayı olarak yorumlayamazsınız. * Ancak CPython'da (ve birçok dil için diğer birçok tercüman için), eğer gerçekten ısrar ediyorsanız, kullanabilir ctypesyüklemek için libpython, bir nesnenin döküm ida POINTER(Py_Object)ve sızıntı tür sistemi zorlar. Bunun tür sistemini zayıflatıp azaltmadığı kullanım durumlarınıza bağlıdır; güvenliği sağlamak için dil kısıtlamalı bir yürütme sanal alanı uygulamaya çalışıyorsanız, bu tür kaçışlarla uğraşmanız gerekir ...

* struct.unpackBaytları okumak ve "C'nin bu baytları nasıl temsil edeceğinden" yeni bir int oluşturmak gibi bir işlev kullanabilirsiniz , ancak bu açıkça sızıntı yapmaz; Haskell bile buna izin veriyor.


Bu arada, örtük dönüşüm gerçekten zayıf veya sızdıran tip sisteminden farklı bir şeydir.

Her dil, hatta Haskell bile, bir tamsayıyı bir dizgiye veya bir kayan noktaya dönüştürme işlevlerine sahiptir. Ancak bazı diller bu dönüşümlerden bazılarını sizin için otomatik olarak yapar; örneğin, C olarak, bir işlev isteyen bir işlev çağırırsanız floatve iletirsenizint , sizin için dönüştürülür. Bu kesinlikle, örneğin beklenmedik taşmalara neden olan hatalara yol açabilir, ancak bunlar zayıf tip bir sistemden aldığınız aynı tür hatalar değildir. Ve C burada gerçekten daha zayıf değil; Haskell'de bir int ve bir şamandıra ekleyebilir veya hatta bir katarı bir dizeye birleştirebilirsiniz, daha açık bir şekilde yapmanız gerekir.

Ve dinamik dillerle, bu oldukça bulanık. Python veya Perl'de "şamandıra isteyen bir işlev" diye bir şey yoktur. Ancak, farklı türlerde farklı şeyler yapan aşırı yüklenmiş işlevler vardır ve örneğin, başka bir şeye dize eklemenin "dize isteyen bir işlev" olduğu konusunda güçlü bir sezgisel his vardır. Bu anlamda, Perl, Tcl ve JavaScript örtülü dönüşüm (bir çok şey gibi görünen "a" + 1verir "a1"Python çok az yapar iken (,) "a" + 1bir özel durum, ama 1.0 + 1sen veriyor 2.0*). Bu duyguyu biçimsel terimlere sokmak zordur - neden +dizinleme gibi başka işlevler varsa, bir dize ve int alan bir şey olmamalı ?

* Aslında, modern Python'da bu, OO alt tipi açısından açıklanabilir, çünkü isinstance(2, numbers.Real)doğrudur. Ben 2Perl veya JavaScript dize türü bir örneği olduğu herhangi bir anlamı olduğunu sanmıyorum ... Tcl her ne kadar, aslında, her şey bir dize örneği olduğundan.


Son olarak, "güçlü" ile güçlü / esnek / ifade anlamına gelen "güçlü" ve "zayıf" yazmanın başka, tamamen dikey bir tanımı daha vardır.

Örneğin, Haskell, bir sayı, bir dize, bu tür bir liste veya dizelerden bu türe bir harita olan bir tür tanımlamanıza izin verir; bu, JSON'dan kodu çözülebilecek her şeyi temsil etmenin mükemmel bir yoludur. Java'da böyle bir tür tanımlamanın bir yolu yoktur. Ancak en azından Java'nın parametrik (jenerik) türleri vardır, bu nedenle T Listesi alan bir öğeyi yazabilir ve öğelerin T türünde olduğunu bilirsiniz; erken Java gibi diğer diller sizi bir Nesne Listesi ve mahzun kullanmaya zorladı. Ancak en azından Java, kendi yöntemleriyle yeni türler oluşturmanıza izin verir; C sadece yapılar oluşturmanıza izin verir. BCPL de buna sahip değildi. Ve böylece sadece tiplerin farklı bit uzunlukları olduğu montaja kadar.

Yani, bu anlamda, Haskell'in tip sistemi, önceki Java'lardan daha güçlü olan ve BC'lerden daha güçlü olan C'lerden daha güçlü olan modern Java'lardan daha güçlüdür.

Peki, Python bu spektruma nerede uyuyor? Bu biraz zor. Çoğu durumda, ördek yazmak Haskell'de yapabileceğiniz her şeyi ve hatta yapamayacağınız bazı şeyleri simüle etmenizi sağlar; emin olun, hatalar derleme zamanı yerine çalışma zamanında yakalanır, ancak yine de yakalanırlar. Bununla birlikte, ördek yazmanın yeterli olmadığı durumlar vardır. Örneğin, Haskell'de boş bir ints listesinin bir ints listesi olduğunu söyleyebilirsiniz, böylece +bu listeyi azaltmanın 0 * döndürmesi gerektiğine karar verebilirsiniz ; Python'da boş bir liste boş bir listedir; azaltmanın ne +yapması gerektiğine karar vermenize yardımcı olacak tür bilgisi yoktur.

* Aslında, Haskell bunu yapmanıza izin vermiyor; boş bir listede başlangıç ​​değeri almayan azaltma işlevini çağırırsanız bir hata alırsınız. Ancak yazı sistemi, bu işi yapabileceğiniz kadar güçlü ve Python's değil.


3
Bu cevap harika! Listenin en altında bu kadar uzun süre utanç verici.
LeoR

1
C örneğinize sadece küçük bir yorum: char sz[]char için bir işaretçi değil, char dizisidir ve atamada işaretçiye bozunur.
majkel.mk

39

'Güçlü yazılmış' ile 'dinamik yazılmış' ile karıştırıyorsunuz .

1Dize ekleyerek türünü değiştiremiyorum '12', ancak bir değişkente hangi türleri sakladığımı seçebilir ve programın çalışma süresi boyunca değiştirebilirim.

Dinamik yazmanın tersi statik yazımdır; değişken türleri beyanı bir programın ömrü boyunca değişmez. Güçlü yazmanın tersi zayıf yazımdır; değerlerin türü bir programın ömrü boyunca değişebilir.


Bağlantıdaki açıklama güçlü bir şekilde yazılmıştır: "Genel olarak, güçlü bir şekilde yazılan bir dilin derleme sırasında daha katı yazım kuralları vardır, bu da derleme sırasında hataların ve istisnaların gerçekleşme olasılığının daha yüksek olduğu anlamına gelir." Python zayıf yazılmış bir dildir ... wiki yanlış mı?
Raining

1
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼: bu hiç ima edilmedi. Python'un derleme zamanında katı yazım kuralları vardır, oluşturulan her nesnenin sadece bir türü vardır. Ve 'genel olarak' hiçbir şey ifade etmez, sadece Python'un bir istisna olduğu anlamına gelir.
Martijn Pieters

24

Bu wiki Python makalesine göre Python hem dinamik hem de güçlü bir şekilde yazılmıştır (iyi bir açıklama da sağlar).

Belki de program yürütme sırasında türlerin değiştirilemediği ve olası hataları algılamak için derleme sırasında tür denetlemesinin yapıldığı statik olarak yazılan dilleri düşünüyorsunuzdur .

Bu SO sorusu ilginizi çekebilir: Dinamik tür dillere karşı statik tür diller ve Tür Sistemleri hakkındaki bu Wikipedia makalesi daha fazla bilgi sağlar


18

TLDR;

Python'un yazarak olan Dinamik Bir int bir dize değişkeni değiştirebilir böylece

x = 'somestring'
x = 50

Python yazarak olduğunu Güçlü Eğer türlerini birleştiremezsiniz böylece:

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

Zayıf yazılan Javascript'te bu olur ...

 'foo'+3 = 'foo3'

Tür Çıkarımına İlişkin

Java sizi nesne türlerinizi açıkça bildirmeye zorlar

int x = 50

Kotlin , bunun birint

x = 50

Ancak her iki dil de statik tür kullandığından x, bir int. Her iki dil de aşağıdaki gibi dinamik bir değişikliğe izin vermez:

x = 50
x = 'now a string'

Javascript ayrıntılarını bilmiyorum ama aşırı yükleme ve sahne arkasında tip dönüşüm yapıyor 'x' + 3olabilir operator+?
Raining

3
Her neyse, cevabınız aslında yukarıdakilerden daha özlü ve anlaşılması kolaydır.
Raining

8

Zaten birkaç kez cevaplandı, ancak Python güçlü bir şekilde yazılmış bir dildir:

>>> 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'te aşağıdakiler:

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

Zayıf yazma ile güçlü yazma arasındaki fark budur. Zayıf türler, bağlama göre (örneğin Perl) otomatik olarak bir türden diğerine dönüştürmeyi dener. Güçlü türler asla dolaylı olarak dönüştürülmez.

Karışıklıklarınız Python'un değerleri isimlere (genellikle değişkenler olarak adlandırılır) nasıl bağladığının yanlış anlaşılmasında yatar.

Python'da adların türü yoktur, bu nedenle aşağıdakileri yapabilirsiniz:

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

Ve isimler herhangi bir şeye bağlanabilir:

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

Daha fazla okuma için:

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

ve biraz ilgili ama daha gelişmiş:

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


1
Birkaç yıl sonra - başka bir yararlı ve ilgili kaynak: youtu.be/_AEJHKGk9ns
Wayne Werner

Güçlü ve zayıf yazmanın, 3 + '4' gibi sonuç türleriyle alakası yoktur. JavaScript, bu örnek için Python kadar güçlüdür.
qznc

@qznc Javasript ne kadar güçlü? Sonuçta ortaya çıkan tür ile ilgisi olduğunu düşündüğümü sanmıyorum, gerçekten açıkça zayıf türlerin otomatik olarak bir türden diğerine dönüştürmeye çalıştığını belirtiyorum .
Wayne Werner

2
@oneloop mutlaka doğru değildir, sadece şamandıraları ve intsları birleştirme davranışı iyi tanımlanmıştır ve bir şamandıra ile sonuçlanır. Sen yapabilirsin "3"*4de Python. Tabii ki sonucudur "3333". Her iki şeyi de dönüştürdüğünü söyleyemezsiniz. Tabii ki bu sadece anlambilimi savunuyor olabilir.
Wayne Werner

1
Bu Python üretir çünkü mutlaka doğru değil @oneloop floatkombinasyonu dışında floatve intdolaylı türünü dönüştürme emin olabiliyoruz. Şamandıra ve int arasında doğal bir ilişki vardır ve aslında heirarşi türü bunu ortaya çıkarır. Javascript düşüncelerini tartışabileceğinizi '3'+4ve 'e'+4her ikisinin de Python'un 3.0 + 4iyi tanımlanmış olduğunu düşündüğü şekilde iyi tanımlanmış işlemler olabileceğini varsayıyorum , ancak o zaman güçlü veya zayıf türler gibi bir şey yok, sadece (un) tanımlanmış operasyonlar.
Wayne Werner

6

Python değişkeni, değeri temsil eden hedef nesneye türlenmemiş bir başvuruyu saklar.

Herhangi bir atama işlemi, atanmamış nesneye türlenmemiş referansın atanması anlamına gelir - yani nesne orijinal ve yeni (sayılan) referanslar aracılığıyla paylaşılır.

Değer türü, referans değere değil, hedef nesneye bağlıdır. (Güçlü) tip kontrolü, değere sahip bir işlem gerçekleştirildiğinde (çalışma süresi) yapılır.

Başka bir deyişle, değişkenlerin (teknik olarak) türü yoktur - kesin olmak isterse değişken türü açısından düşünmek mantıklı değildir. Ancak referanslar otomatik olarak kayıttan çıkarılır ve aslında hedef nesnenin türü açısından düşünürüz.


6

"Güçlü yazım" teriminin kesin bir tanımı yoktur.

Bu nedenle, terimin kullanımı kiminle konuştuğunuza bağlıdır.

Bir değişkenin türünün açıkça bildirilmediği veya statik olarak güçlü bir şekilde girileceği herhangi bir dili düşünmüyorum.

Güçlü yazım yalnızca dönüşümü engellemez (örneğin, bir tamsayıdan bir dizeye "otomatik olarak" dönüştürme). Atamayı engeller (yani bir değişkenin türünü değiştirmek).

Aşağıdaki kod derlenirse (yorumlanır), dil güçlü yazılmaz:

Foo = 1 Foo = "1"

Güçlü yazılan bir dilde, bir programcı bir türe "güvenebilir".

Örneğin, bir programcı beyanı görürse,

UINT64 kZarkCount;

ve 20 satır sonra kZarkCount'un (aynı blokta olduğu sürece) hala bir UINT64 olduğunu - müdahale eden kodu incelemek zorunda kalmadan biliyor.


1

Ezberlemek için mükemmel bir kısa yol keşfettim:

Dinamik / statik yazım; güçlü / zayıf tip değer.


0

Bence, bu basit örnek güçlü ve dinamik yazım arasındaki farkları açıklamalısınız:

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

Java statik yazmayı gösterirken, python kodunuz dinamik yazmayı gösterir. Daha iyi bir örnek $ var = '2' + 1 // olur 3 sonuç
erichlf

@ivleph katılıyorum. ayrıca şöyle bir şey yazmak da mümkündür: "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....

Yukarıdakiler, uzun bir süre boyunca büyük bir sistemde sürdürülemez bir kod kabusu yaratacaktır. İstediğiniz gibi adlandırın, ancak bir değişken türünü "dinamik olarak" değiştirme yeteneği sadece kötü bir fikirdir ...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.