Python mantıksal işleçler sorusu

a = "False" değerini alan bir string değişkenimiz var. Bunu bool sınıfına convert etmek için, bool(a) ifadesini giriyoruz ve yeni oluşan değer bize bool sınıfında True değerini döndürüyor. Dediğin doğru olmakla birlikte yanlış. Bool sınıfı 2 değer döndürüyor evet, string bir değişkeni bool’a çevirdiğimizde yeni oluşan değer bool tipinde True oluyor.

Evet bir yazım yanlışı mevcut, onu şöyle düzelteyim. “Boolean sınıfında değeri boş olmayan bir string değişken her zaman True dur.”
Eğer mantıktan gidiyorsak, bool olarak False değeri olan integer 0 sayısı bool tipinde etkisizdir, yani hiçtir. Böyle bir mantıkla bakıldığında None tipini de int 0 sayısına a = "" str değişkenini de “hiç” olarak görmek mümkün. Yani bütün hiçler bool’da False değerinde. Başlangıçta bütün “hiçler” False değerini alıyor. 0’dan farklı yani “hiç” olmayan tüm ifadeler başlangıçta True dur. Varlık ve yokluğu tanımlıyor gibi de düşünebilir Bool sınıfı. 1 < 2 varolan bir şeydir bu yüzden “True”. 2<1 var olmayan bir şeydir bu yüzden False

Eğer bu mantıklı olsaydı Bool sınıfı doğru ve yanlışı ölçen bir sınıf olmaktan çıkardı.

Evet belki görüntüde yok, ama mantıkta böyle bir dönüşüm mevcut.

python - Most idiomatic way to convert None to empty string? - Stack Overflow böyle bir bağlantı buldum iş görür mü ?

a = "" değişkeni None değerine sahip string bir değişken değil mi yani? Evet a bir değişken ama aldığı değer boş. b = None ve a != b bu daha da mantıksız.

Edit: bağlantıda None tipinden boş string değerine çevirme fonksiyonu yer alıyor. Herhangi bir ifadenin None tipine çevirileceğini bende düşünmüyorum.

Hayır, a = "" ifadesinde a değişkeninin değeri "". a değişkeni hala str sınıfına ait bir nesne.

Fakat b = None dediğimizde, artık b değişkeninin tipi NoneType’dır.

Fakat aşağıdaki her iki sorguda da ekrana False yazısı yazdırılacaktır.

a = ""
if a: print(True)
else: print(False)
b = None
if b: print(True)
else: print(False)

Benzer bir sonuç alıyor olmamız, a’nın değerinin None olduğu anlamına gelmez. a bir str örneğidir. b ise bir NoneType örneğidir.

1 Beğeni

bool sınıfı, int sınıfını miras olarak alır. Dolayısıyla aslında bool("Hello") şeklinde yazılmış bir kod saçma bir kod.

Çünkü biliyoruz ki, uzunluğu 0 olmayan herhangi bir veri tipini bool sınıfına argüman olarak verirsek bize True değerini geri döndürecektir.

Yani str için:

"Hello" -> 1
"" -> 0
" " -> 1

Aynı durum diğer bütün veri tipleri için de geçerli.

Mantıktan gidiyoruz diye böyle bir örnek gösterdim. Yoksa a değişkeninin bir string tipinde değişken olduğunu ve None tipindeki b değişkeniyle eşit olmayacağını biliyorum.

Tamam, yanlis anlasilmanin kaynagini buldum.

bool fonksiyonunu bool sinifinin constructor’i olarak dusunuyorsun. Bu tek basina bir sorun degil (hatta yanlis oldugunu savunamam). Fakat bu fonksiyona arguman olarak gidebilen her seyi bool sinifinin bir elemani olarak goruyorsun, yanlis orada basliyor.

bool sinifinin iki adet elemani var: True ve False.

Bazi diller butun siniflarina ekstra bir eleman ekliyor (null). Python bu dillerden biri degil.

“Bool sinifinin iki ornegi (instance) var” veya “bool tipinin iki olasi degeri var”.

Bu yanlisi gecen cevapta duzeltmistim, tekrar ugrasmayacagim.

Boolean sinifinda string degisken?
“Yesil elma olmayan her portakal kirmizi elmadir”

Siniflarin/turlerin degil, monoid’lerin (sinif+binary islem) etkisiz elemani olur. Mesela sayilarda + isleminin etkisiz (identity) elemani 0. Ama * isleminin etkisiz elemani 1. Benzer sekilde, or islemininki False ve and islemininki True. (Hatta cogunlukla yazarken and/ yerine ·, or/ yerine + kullaniyorum.)

None degerini (tipi NoneType) int’teki butun sayilar olarak gormek de mumkun (cunku kendisi NoneType’daki butun degerler) hic bir sayi olarak gormek de mumkun (cunku kendisi int degil).

“Bir tipi baska bir tip olarak gormek” gibi nasil olmasi gerektigi konusunda anlasamadigimiz, her programlama dilinin farkli bir sekilde uyguladigi eylemi neden yapmak zorundayiz onu anlamis degilim. int int, NoneType da NoneType.

Bu arada:

>>> int(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'

Burada Python’in verdigi kararin arkasindaki mantigi aciklamissin, tesekkur ediyorum. Ama ben bu mantigi zaten goruyorum ve yanlis oldugunu one suruyorum. Yani argumanimin cikis noktasini bana cevap olarak yazmis oluyorsun.

Evet, tesekkurler, dedigimi pekistiriyor :slight_smile: Eger boyle bir degisim olsaydi birine bakip digerini donduren bir fonksiyon yazilmak zorunda kalinmazdi.

Ama burada bahsettigim degisim tur degisimi idi. Python’da bir tek if/while’a giren ve __exit__'ten cikan seylerin bool’a degisimini biliyorum. (Bir de deger dondurmeyen fonksiyonlar None donduruyor ama.

Hah! Evet! Orijinal argumanim bu. Turleri otomatik olarak birbirine cevirmeye baslarsan olusan mantiksizliklarin/tutarsizliklarin ucu bucagi yok.

__len__ kontrol edilmeden once __bool__ kontrol ediliyor: object.__bool__

Onceden belirtirsen iyi olur, ben de yanlis anladim.

Yanlis ifadelerle temel seyleri bilmeyen insanlar arasinda kayniyor boyle “X icin Y dedim, aslinda Y degil” ifadeleri.

(Teknik forumlarda tartismanin zorlugu da burada bu arada.)

Ben bunun Python’da çok da büyük bir problem olduğunu sanmıyorum. if var: ... çok fazla kullanılan bir yapı, amacı da çoğunlukla değişkenin uzunluğu üzerinden işlem yapmak oluyor. boola otomatik çevrilmesi de Python tarafından (dediğiniz gibi) __bool__ ve __len__ üzerinden yapılıyor. Bunlar zaten kendi sınıflarımızı istediğimiz gibi bool fonksiyonu ile kullanmamızı sağlıyor. Eğer var değişkeni "False" olduğunda if var: ... şartının sağlanmasını gerçekten istiyorsak if var == "False": ... kullanmamızın önünde hiçbir engel yok. Hatta bunun için standart kütüphaneye bir şeyler eklemişler, gereksiz bence.

Ayrıca ben Python’daki otomatik tür değişiminde geçerli sebepler olduğunu düşünüyorum. Sonuçta dili geliştirenler de mantıklı düşünüyordu, düşünmeliler.

from timeit import timeit

def outo(var):
    for i in range(1000000):
        if var:
            pass

def call(var):
    for i in range(1000000):
        if bool(var):
            pass

print("auto", timeit("outo(1)", globals = globals(), number = 1))
print("call", timeit("call(1)", globals = globals(), number = 1))
print("auto", timeit("outo(0)", globals = globals(), number = 1))
print("call", timeit("call(0)", globals = globals(), number = 1))

print("auto", timeit("outo([1, 2])", globals = globals(), number = 1))
print("call", timeit("call([1, 2])", globals = globals(), number = 1))
print("auto", timeit("outo([])", globals = globals(), number = 1))
print("call", timeit("call([])", globals = globals(), number = 1))

Her ne kadar Python hızı için kullanılacak bir dil olmasa da yapısal programlamada çok önemli bir yere sahip olan koşulların çok da yavaş olmaması lazım.


Eğer bool fonksiyonu verilen parametrenin içerdiklerinin niteliklerine bakacaksa

bool(["F", "a", "l", "s", "e"]) == bool("False") == False

da olması lazım.

Benim şahsi fikrim Python’un genelini göz önünde bulundurduğumuzda bool fonksiyonun bu şekilde çalışmasının ve koşullarda otomatik bool’a çevirmenin gayet mantıklı olduğu yönünde, farklı dillerin farklı paradigmaları ve yöntemleri izlemesi de çok doğal. Niçin bir sürü dil var?


Bence mantıkta da mevcut değil. None’un dildeki işlevi farklı. Buradaki işlem string’in uzunluğu ile alakalı.

@aib’in dediğini destekleme açısından iş görüyor :slightly_smiling_face:

Eğer ciddi düşünecek olursak "" boş falan değil (None hiç değil), sadece uzunluğu 0. Artık sıfıra boş deyip dememek size kalmış…

>>> import sys
>>> sys.getsizeof("")
49

Ayrıca karşılaştırma işleçleri tür değişimi yapmıyor.

:+1: Ama “NoneType’deki tek değer” daha uygun olmaz mı?

Bunu okuyunca aklıma string’lerin hafızada nasıl saklandığı geldi. bool’un int’i miras aldığını düşününce çok da saçma değil sanki.

Saçma kelimesi belki biraz yersiz oldu, onun yerine gereksiz kelimesini kullanmak daha uygun olabilir.

def f(arg: str = ""):
    if arg:
        [...]
    else:
        [...]

Şu ana kadar bool("hello") şeklinde bir kullanımla karşılaşmadım. Yani yukarıdaki kodu şöyle yazmak bana gereksiz geliyor:

def f(arg: str = ""):
    if bool(arg):
        [...]
    else:
        [...]

Amac “degiskenin uzunlugu 0’dan buyukse” islem yapmak ise bunu if len(var) > 0: ile kontrol etmek daha dogru; ayni isi yapiyorlar ve—bir dakika, ayni isi yaptiklarindan emin miyiz? Hmm Python dil standardina bakiyoruz, ve…vakit kaybettik bile.

Ilk form kodu okuyan kisinin dilin incelikleriyle hasir-nesir olmasini gerektiriyor. Ikincisini okumak icin sentaksi bilmek yeterli.

Insanin bildigi dil sayisi arttikca bu tur idiosinkrasileri akilda tutma becerisi ve istegi dusuyor. (Ve bu yuzden kodun cok dil bilen biri tarafindan rahatca okunabilmesini istiyorsak kullanmamaliyiz)

Mesela bu kod neyi kontrol ediyor? Code review’dan gecirir misin? :

x**2*2**y > 128 || y && z

PHP’yi tasarlayanlar da mantikli oldugunu dusunuyorlardi. Sonra boyle tablolar cikti. :man_facepalming:

(Bi tane de favori bug’im vardi, tur degisimine izin veren == kullanildigi icin iki farkli cryptographic hash esit oluyordu. Edit: Startpage Search Results)

Ama bu cok daha buyuk (ve oznel) bir static vs. weak typing tartismasi; girmeyelim, evet.

Sunu da birakip kacayim:

>>> "true" if math.nan else "false"
'true'
1 Beğeni

Aslında böyle bir şey yapmıyorum. Bir değişkeni Boolean sınıfına çevirme işlemi yapıyorum. string a değişkenini bool(a) fonksiyonu ile bool tipinde True ya da False bir değere çeviriyorum. Boolean sınıfının sizin ve benimde belirttiğim gibi iki değeri var; ki zaten dediğiniz gibi True(1) ya da False(0) elemanlarından başka hiçbir değer alması söz konusu değil. Yine sizinde belirttiğiniz gibi ;

değeri Python dilinde Boolean sınıfına ait bir değer değil.

Eğer yazdığımı bir bütün olarak alırsanız burada bir yanlışlıktan ziyade, tekrar edilmemiş bir “kelime” eksikliğini görmüş olmanız gerekir. Siz düzeltmediniz ben tekrar düzelteyim.

Boolean sınıfına çevirilen ve değeri "" (boş) olmayan string bir değişken bool sınıfında True değerini alır.

Evet aslında tam olarak böyle. TextType , SetType ve SequenceType dışındaki bütün data tiplerinde aldığı değer 0 olmayan bütün değerlerin Bool sınıfına çevrildiğinde aldığı değer size göre;

Boolean sınıfına göre ise True olacaktır.
Daha da açmak gerekirse;
Text tipindeki "0" değerinin Boolean sınıfında alacağı değer True iken,
Numeric tipindeki 0 , 0.0 , 0j değerlerinin Boolean sınıfına çevrildiğinde alacağı değer False,
Sequence tipindeki ["0"], ("0","1"), range(1) değerinin Boolean sınıfında karşılığı True, buraya bir ek parantez daha açıyorum range(0) 0, 0 değerini verdiği için False değerini alıyor.
Mapping tipindeki {"a" : 0} değeri Boolean sınıfına çevriminde alacağı değer True,
Set tipindeki {"0"}, frozenset({"0"}) değerleri Boolean sınıfına çevrildiğinde alacağı değer yine True,
Binary tipindeki b"0", bytearray(1), memoryview(bytes(1)) değerlerinin Boolean sınıfına çevrildiğinde alacağı değerler True olacaktır. Buraya da bir ek parantez açayım, binary tipinde oluşturulan bytearray değişkenine bytearray(0) değeri verildiğinde bytearray(b'') değeri alındığı için boolean sınıfında alacağı değer 0 yani false oluyor.

integer NumericType’da alınacak 3 değerden biri iken, None NoneType’da alınacak tek değerdir. NoneType sınıfında None dışında başka bir değer yoktur. None bir obje iken NoneType None objesinin tipidir.

Bende belirtmiştim zaten bunu. Var olan bir şeyi hiçe çevirmek zannımca hiçbir mantığa uymaz :slight_smile: Burada biraz felsefeye de giriş olacak belki ama Hiçlik var olabilir ama Varlık hiç olamaz. Bir şey varsa vardır ve bunu “hiç” yapamayız, ama “hiç” olan bir şey “var” olabilir. Vermiş olduğum linkte "hiç"i var ediyor, yani bir None değeri, değeri TextType olan ama string olarak değeri “hiç” olan bir değişkene çeviriyor.

Evet burada çok haklısınız, üzgün olduğumu belirterek özür diliyorum. TextType indeki string bir değer ile NoneType bir None değer asla eşit olamaz. Biri hiç iken, diğeri varolan bir şeyin hiçi.

Buradaki tartışmanın bitmeyeceğini düşünüyorum ancak yine de Python dilindeki Boolean data tipinin Numeric data tipinden 1 ve 0 'ı miras almasını şöyle yorumlamayı tercih ediyorum. 0 hiçliği ve yanlışlığı tasvir ediyor, 1 ise varlığı ve doğruluğu.
None değeri hiçtir ve bu yüzden Bool tipinde karşılığı False dir.
"None" değeri ise Text tipine ait string değer olarak “None” değeri almış bir objedir. Bu yüzden Bool olarak True değeri alır. (Aynı şekilde "False" değeri almış string obje de True)
"" değeri Text tipinde string bir obje olarak, objenin içeriği hiç olmasından Bool tipindeki karşılığı False oluyor. Yani string bir objesin, ama aynı zamanda "hiç"e sahip bir objesin ve bu yüzden False sun.
Aynı şekilde diğer tüm(Numeric ve Sequence tipinin range() objesi hariç) data tiplerinde aldığı değer şayet "hiç"se ( [], (), {}, frozenset({}), b"", bytearray(), memoryview(bytes()) ) Bool sınıfında alacağı değer False olacaktır.

Evet, bunu fark ettikten sonra yukari geri cikip yazdigimi silmeyi unuttum, ozur dilerim.

Bunu bilmiyordum, diger jeneratorlerden farkli calisiyormus o zaman:

def b():
    if False:
        yield 1

print("true" if b() else "false") # true
print("true" if map(lambda x: x, range(0)) else "false") # true
print("true" if filter(lambda _: False, range(0)) else "false") # true
print("true" if [x for x in range(0)] else "false") # false

Dedigim gibi, otomatik tur cevrimi varsa tutarlilik beklememek gerek.

Ben bunu mantiksizlik ornegi olarak yazmistim :S

NumericType (ve hatta

) nedir bilemiyorum. numbers.Number gibi bir ABC’tan mi bahsediyoruz?

Soyledigim seyin bu tekrarinin ne manaya geldigini de anlayamadim :frowning:

Felsefeye kayacagimiza type theory’e kayalim:

NoneType turu aslinda hicligi degil, tekligi/tekilligi temsil eden “Unit” turunun izomorfizmi. (None da onun tek degeri, “()”)

Hicligi temsil eden tur “Void” ve hic bir degeri yok. (Bazen “bottom” () adi verilen ve bitmeyen hesaplamalari temsil eden bir deger veriliyor.)

Varliktan hiclik uretemememiz * -> Void metaturunun Void disinda hic bir turle doldurulamamasiyla ifade ediliyor. (Hic bir fonksiyon Void donduremiyor cunku Void’e ait hic bir deger yok.)

Void -> Void (teorik) fonksiyonu mumkun cunku bu fonksiyonu cagiramayacagimiz icin, bir deger dondurmesine gerek yok.

Hatta Void -> * genel olarak mumkun. Bu da Turkcesini bulamadigim EFQ (“yanlistan her sey dogar”) prensibini temsil ediyor.

Unit donduren fonksiyonlar (* -> Unit) mumkun fakat pek bir ise yaramiyorlar. Hatta hepsi ayni:

foo :: forall a. a -> ()
foo _ = ()

Buradan None donduren fonksiyonlarin pure olmadiklarini ve sadece side effect’leri icin calistirildiklarini cikartabiliriz.

Unit alan fonksiyonlar (Unit -> *) ise cuzi cunku sadece tek sekilde cagirilabiliyorlar. Hatta dondurdukleri degere esdeger sayilabilirler.

Bu noktada duruyorum cunku herkesi kaybettigimi dusunuyorum. Ilgilenen olursa bunun ilerisinde turler cebiri var, product ve coproduct meta turleriyle beraber (mesela) int alip bool donduren kac degisik fonksiyon olabilecegini hesaplayabiliyoruz.

1 Beğeni

Evet, ben de Python’in muhtelif sacma kurallarini tekrar tekrar duymaktan sikildim acikcasi.

Diger cevrim kurallarini gormus (hatta hepsini yaratabilen, veya en azindan sayabilen :​) biri de neden diger hepsinin yanlis ve Python’inkinin dogru oldugunu anlatmadan Python’inkinin dogru olduguna inanmayacagim.

Kesinlikle katılıyorum.

Numeric tipi 3 değişkene(int, float, complex) sahip bir data tipidir.
Set tipi 2 değişkene(set, frozenset) sahip bir data tipidir.
Sequence tipi 3 değişkene(list, tuple, range) sahip bir data tipidir.

Ek olarak;
Text tipi değişkeni str,
Mapping tipi değişkeni dict,
Boolean tipi değişkeni bool,
Binary tipinin değişkenleri de bytes, bytearray, memoryview.

Buna da ek olarak ;
NoneType sadece None (değeri alan) None sözcüğüne sahip bir data tipidir.
a = None bir değişken olur mu emin olamadığım için, yukarıda “sözcüğüne” ifadesini kullandım.

Gerçekten ilk defa duyduğum bir teori. Vesilenizle bunu da araştırılacaklar kuyruğuna ekleyeceğim.
@aib , @dildeolupbiten , @EkremDincel hocalarıma tartışma için teşekkür ederim. Bu benim bu konu için son mesajım.

Bana daha çok iter kullanıyormuş gibi geldi, @keyshout’un verdiği bilginin yanlış olduğunu düşünüyorum:

>>> range(0) == range(2, 1, 3)
True
>>> bool(range(0))
False
>>> bool(range(2, 1, 3))
False
>>> list(range(2, 1, 3))
[]
>>> list(range(0))
[]

Bunu görünce bir şey deniyesim geldi de, map sınıfı da biraz garipmiş, döndürülen nesnenin bool değeri hep True oluyor:

>>> map_type = map(lambda _: None, []).__class__
>>> bool(map(lambda x: x, []))
True
>>> list(map(lambda x: x, []))
[]
>>> bool(iter(map(lambda x: x, [])))
True
>>> hasattr(map_type, "__bool__")
False
>>> hasattr(map_type, "__len__")
False
>>> hasattr(map_type, "__iter__")
True

3’e gecerken lazy/generator yaptilar bir suru seyle beraber, dict.items() gibi. (Miydi? Eski eager versiyonlarinin basina x koyulan metodlari diyorum, “xitems” filan).

Fakat ne hikmetse lazy sekanslarin nasil davrandiklarini unuttular. bool conversion’i cok umrumda degil (dedigim gibi, kontrol edilmek istenen seyin implicit degil explicit olmasi gerektigini dusunuyorum) ama len yok!

>>> len(range(100))
100
>>> len(filter(lambda x:x%2==0, range(100)))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'filter' has no len()
>>> len(list(filter(lambda x:x%2==0, range(100))))
50
Bu arada uzerinden cok zaman/proje gectigi icin emin olmamakla beraber, sanirim ilk ciktiklarinda `len` 1 donduruyordu! (**1** tane `map`/`filter` objesi ya) Baya gol yemistim; hatanin dile ait ve `len . map` kadar basit bir yerde olabilecegini dusunene kadar kodu yarim saat boyunca didik didik etmistim.

Edit: 3’te len hic bir zaman olmamis, demek ki benim karsilastigim hata baska bir seymis. Hem de implicit conversion’lari sevmeyen biri olarak, dusunun.

14:14:35 0 aib@vivaldi:/tmp% python2 -c 'print("true" if map(lambda x:x, range(0)) else "false")'
false
14:14:37 0 aib@vivaldi:/tmp% python3 -c 'print("true" if map(lambda x:x, range(0)) else "false")'
true

burada gerçekten bir mantıksızlık varmış gibi hissediyorum
ama madem o kod da true dönüyor

if False:
  print(1)
else:
  print(2) # sonuc 2

bu kodda neden çıktımız 2 oluyor