Bagaimana "dan" dan "atau" bertindak dengan nilai non-boolean?

99

Saya mencoba belajar python dan menemukan beberapa kode yang bagus dan pendek tetapi tidak sepenuhnya masuk akal

konteksnya adalah:

def fn(*args):
    return len(args) and max(args)-min(args)

Saya mengerti apa yang dilakukannya, tetapi mengapa python melakukan ini - yaitu mengembalikan nilai daripada Benar / Salah?

10 and 7-2

mengembalikan 5. Demikian pula, mengubah dan ke atau akan mengakibatkan perubahan fungsionalitas. Begitu

10 or 7 - 2

Akan kembali 10.

Apakah ini gaya yang sah / dapat diandalkan, atau adakah alasan lain dalam hal ini?

Marcin
sumber
1
and(serta or) tidak dibatasi untuk bekerja dengan, atau mengembalikan nilai boolean.
cs95
1
IMNSHO: itu cara penulisan yang agak membingungkan; Saya tidak tahu langsung apakah itu seharusnya mengembalikan boolean (apakah ada min dan max yang berbeda) atau angka (apa perbedaan min dan max). Jika yang terakhir, maka ada juga pertanyaan apakah masuk akal untuk memberikan selisih daftar panjang-nol itu sebagai angka. (Alih-alih Noneatau pengecualian)
ilkkachu
7
Ini berfungsi, seperti yang telah dijelaskan orang lain, namun satu masalah yang mungkin adalah jika kembali 0Anda tidak dapat mengetahui apakah argskosong atau tidak kosong tetapi memiliki semua elemen yang sama.
Terutama Kapur
@EspecialLime: tepatnya. Saya telah menyebutkannya dalam jawaban saya .
Eric Duminil

Jawaban:

135

TL; DR

Kami mulai dengan meringkas dua perilaku dari dua operator logika anddan or. Idiom ini akan menjadi dasar pembahasan kita di bawah ini.

and

Kembalikan nilai Falsy pertama jika ada, jika tidak kembalikan nilai terakhir dalam ekspresi.

or

Kembalikan nilai Truthy pertama jika ada, jika tidak kembalikan nilai terakhir dalam ekspresi.

Perilaku tersebut juga diringkas dalam dokumen , terutama dalam tabel ini:

masukkan deskripsi gambar di sini

Satu-satunya operator yang mengembalikan nilai boolean terlepas dari operannya adalah notoperator.


Evaluasi "Truthiness", dan "Truthy"

Pernyataan

len(args) and max(args) - min(args)

Adalah cara singkat yang sangat pythonic (dan bisa dibilang kurang terbaca) untuk mengatakan "jika argstidak kosong, kembalikan hasil max(args) - min(args)", jika tidak kembalikan 0. Secara umum, ini adalah representasi if-elseekspresi yang lebih ringkas . Sebagai contoh,

exp1 and exp2

Harus (secara kasar) diterjemahkan ke:

r1 = exp1
if r1:
    r1 = exp2

Atau, dengan kata lain,

r1 = exp1 if exp1 else exp2

Demikian pula,

exp1 or exp2

Setara dengan,

r1 = exp1
if not r1:
    r1 = exp2

Di mana exp1dan exp2merupakan objek python sewenang-wenang, atau ekspresi yang mengembalikan beberapa objek. Kunci untuk memahami penggunaan logika anddan oroperator di sini adalah memahami bahwa mereka tidak dibatasi untuk beroperasi pada, atau mengembalikan nilai boolean. Objek apa pun dengan nilai kebenaran dapat diuji di sini. Ini termasuk int, str, list, dict, tuple, set, NoneType, dan ditetapkan pengguna objek. Aturan korsleting masih berlaku juga.

Tapi apakah kebenaran itu?
Ini mengacu pada bagaimana objek dievaluasi ketika digunakan dalam ekspresi bersyarat. @Patrick Haugh merangkum kebenaran dengan baik dalam posting ini .

Semua nilai dianggap "benar" kecuali yang berikut ini, yang "salah":

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] - kosong list
  • {} - kosong dict
  • () - kosong tuple
  • '' - kosong str
  • b'' - kosong bytes
  • set() - kosong set
  • kosong range, sepertirange(0)
  • objek yang
    • obj.__bool__() kembali False
    • obj.__len__() kembali 0

Nilai "kebenaran" akan memenuhi pemeriksaan yang dilakukan oleh pernyataan ifatau while. Kami menggunakan "kebenaran" dan "salah" untuk membedakan dari boolnilai Truedan False.


Bagaimana andBekerja

Kami membangun pertanyaan OP sebagai segue menjadi diskusi tentang bagaimana operator ini dalam hal ini.

Diberikan fungsi dengan definisi

def foo(*args):
    ...

Bagaimana cara mengembalikan perbedaan antara nilai minimum dan maksimum dalam daftar nol atau lebih argumen?

Menemukan minimum dan maksimum itu mudah (gunakan fungsi bawaan!). Satu-satunya halangan di sini adalah menangani kasus sudut dengan tepat di mana daftar argumen bisa kosong (misalnya, memanggil foo()). Kami dapat melakukan keduanya dalam satu baris berkat andoperator:

def foo(*args):
     return len(args) and max(args) - min(args)

foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

Karena anddigunakan, ekspresi kedua juga harus dievaluasi jika yang pertama adalah True. Perhatikan bahwa, jika ekspresi pertama dievaluasi kebenarannya, nilai yang dikembalikan selalu merupakan hasil dari ekspresi kedua . Jika ekspresi pertama dievaluasi sebagai Falsy, maka hasil yang dikembalikan adalah hasil dari ekspresi pertama.

Dalam fungsi di atas, Jika foomenerima satu atau lebih argumen, len(args)lebih besar dari 0(bilangan positif), jadi hasil yang dikembalikan adalah max(args) - min(args). OTOH, jika tidak ada argumen yang dilewatkan, len(args)adalah 0yang Falsy, dan 0dikembalikan.

Perhatikan bahwa cara alternatif untuk menulis fungsi ini adalah:

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)

Atau, lebih tepatnya,

def foo(*args):
    return 0 if not args else max(args) - min(args)

Tentu saja, tidak ada dari fungsi ini yang melakukan pemeriksaan tipe apa pun, jadi kecuali Anda benar-benar mempercayai masukan yang diberikan, jangan mengandalkan kesederhanaan konstruksi ini.


Bagaimana orBekerja

Saya menjelaskan cara kerja ordengan cara yang sama dengan contoh yang dibuat-buat.

Diberikan fungsi dengan definisi

def foo(*args):
    ...

Bagaimana Anda menyelesaikan foountuk mengembalikan semua angka 9000?

Kami biasa ormenangani kasus sudut di sini. Kami mendefinisikan foosebagai:

def foo(*args):
     return [x for x in args if x > 9000] or 'No number over 9000!'

foo(9004, 1, 2, 500)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

foomelakukan penyaringan pada daftar untuk mempertahankan semua nomor 9000. Jika ada angka-angka seperti itu, hasil dari pemahaman daftar adalah daftar yang tidak kosong yaitu Truthy, jadi dikembalikan (korsleting dalam tindakan di sini). Jika tidak ada nomor tersebut, maka hasil dari daftar comp []adalah Falsy. Jadi ekspresi kedua sekarang dievaluasi (string yang tidak kosong) dan dikembalikan.

Menggunakan kondisional, kita bisa menulis ulang fungsi ini sebagai,

def foo(*args):
    r = [x for x in args if x > 9000]
    if not r:
        return 'No number over 9000!' 

    return r

Seperti sebelumnya, struktur ini lebih fleksibel dalam hal penanganan error.

cs95
sumber
33
Tidaklah "pythonic" untuk mengorbankan semua kejelasan untuk singkatnya, yang menurut saya adalah kasusnya di sini. Ini bukan konstruksi yang mudah.
DBedrenko
11
Saya pikir orang harus mencatat bahwa ekspresi bersyarat Python telah membuat sintaks ini kurang umum. Saya pasti lebih suka max (args) - min (args) jika len (args) lain 0 ke aslinya.
richardb
3
Yang umum lainnya yang membingungkan pada awalnya, adalah menetapkan nilai jika tidak ada: "some_var = arg or 3"
Erik
12
@Baldrickk sebelum orang mulai melakukan bashing sintaksis ini untuk mendukung operator terner, perlu diingat bahwa ketika datang ke ekspresi kondisi n-ary, operator terner dapat lepas kendali dengan cepat. Misalnya, if ... else (if ... else (if ... else (if ... else ...)))bisa juga ditulis ulang ... and ... and ... and ... and ...dan pada saat itu menjadi sulit untuk membantah keterbacaan untuk kedua kasus.
cs95
4
Mengorbankan kejelasan untuk singkatnya bukanlah hal yang pythonic, tetapi ini tidak melakukannya. Itu idiom yang terkenal. Ini adalah idiom yang harus Anda pelajari, seperti idiom lainnya, tetapi hampir tidak 'mengorbankan kejelasan'.
Miles Rout
18

Mengutip dari Python Docs

Catatan bahwa baik andatau or membatasi yang nilai dan ketik mereka kembali ke Falsedan True, melainkan mengembalikan argumen dievaluasi terakhir . Ini terkadang berguna, misalnya, jika sada string yang harus diganti dengan nilai default jika kosong, ekspresi s or 'foo'menghasilkan nilai yang diinginkan.

Jadi, beginilah cara Python dirancang untuk mengevaluasi ekspresi boolean dan dokumentasi di atas memberi kita wawasan tentang mengapa mereka melakukannya.

Untuk mendapatkan nilai boolean cukup ketik saja.

return bool(len(args) and max(args)-min(args))

Mengapa?

Hubungan arus pendek.

Sebagai contoh:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

Hal yang sama juga berlaku or, yaitu, ia akan mengembalikan ekspresi yang Truthy segera setelah menemukannya, karena mengevaluasi sisa ekspresi itu berlebihan.

Alih-alih mengembalikan hardcore Trueatau False, Python mengembalikan Truthy atau Falsey , yang bagaimanapun juga akan mengevaluasi ke Trueatau False. Anda dapat menggunakan ekspresi apa adanya, dan ini akan tetap berfungsi.


Untuk mengetahui apa itu Truthy and Falsey , periksa jawaban Patrick Haugh

Amit Joki
sumber
7

dan dan atau menjalankan logika boolean, tetapi mengembalikan salah satu nilai aktual saat dibandingkan. Saat menggunakan dan , nilai dievaluasi dalam konteks boolean dari kiri ke kanan. 0, '', [], (), {}, dan None salah dalam konteks boolean; segalanya benar.

Jika semua nilai benar dalam konteks boolean, dan mengembalikan nilai terakhir.

>>> 2 and 5
5
>>> 2 and 5 and 10
10

Jika ada nilai yang salah dalam konteks boolean dan mengembalikan nilai salah pertama.

>>> '' and 5
''
>>> 2 and 0 and 5
0

Jadi kodenya

return len(args) and max(args)-min(args)

mengembalikan nilai max(args)-min(args)ketika ada args lain yang dikembalikannya len(args)yaitu 0.

Nithin Varghese
sumber
5

Apakah ini gaya yang sah / dapat diandalkan, atau adakah alasan lain dalam hal ini?

Ini sah, ini adalah evaluasi sirkuit pendek di mana nilai terakhir dikembalikan.

Anda memberikan contoh yang baik. Fungsi akan kembali 0jika tidak ada argumen yang diteruskan, dan kode tidak harus memeriksa kasus khusus dari tidak ada argumen yang diteruskan.

Cara lain untuk menggunakan ini, adalah dengan default argumen None ke primitif bisa berubah, seperti daftar kosong:

def fn(alist=None):
    alist = alist or []
    ....

Jika beberapa nilai non-kebenaran diteruskan ke alistdefault ke daftar kosong, cara praktis untuk menghindari ifpernyataan dan perangkap argumen default yang bisa berubah

salparadise
sumber
3

Gotchas

Ya, ada beberapa gotcha.

fn() == fn(3) == fn(4, 4)

Pertama, jika fnkembali 0, Anda tidak dapat mengetahui apakah itu dipanggil tanpa parameter apa pun, dengan satu parameter atau dengan beberapa parameter yang sama:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

Apa fnmaksudnya

Kemudian, Python adalah bahasa dinamis. Itu tidak ditentukan di mana pun apa fn, apa inputnya dan seperti apa outputnya. Oleh karena itu, sangat penting untuk memberi nama fungsi dengan benar. Demikian pula, argumen tidak harus dipanggil args. delta(*numbers)atau calculate_range(*numbers)mungkin menjelaskan lebih baik apa fungsi yang seharusnya dilakukan.

Kesalahan argumen

Akhirnya, andoperator logika seharusnya mencegah fungsi gagal jika dipanggil tanpa argumen apa pun. Itu masih gagal jika beberapa argumen bukan angka, meskipun:

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Alternatif yang memungkinkan

Berikut adalah cara menulis fungsi menurut "Lebih mudah meminta maaf daripada izin." prinsip :

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

Sebagai contoh:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

Jika Anda benar-benar tidak ingin memunculkan pengecualian saat deltadipanggil tanpa argumen apa pun, Anda dapat mengembalikan beberapa nilai yang tidak dapat dilakukan sebaliknya (misalnya -1atau None):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1
Eric Duminil
sumber
0

Apakah ini gaya yang sah / dapat diandalkan, atau adakah alasan lain dalam hal ini?

Saya ingin menambahkan pada pertanyaan ini bahwa ini tidak hanya sah dan dapat diandalkan tetapi juga sangat praktis. Berikut ini contoh sederhananya:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

Oleh karena itu, Anda benar-benar dapat menggunakannya untuk keuntungan Anda. Agar lebih berhati-hati, inilah cara saya melihatnya:

Or operator

Operator Python ormengembalikan nilai Truth-y pertama, atau nilai terakhir, dan berhenti

And operator

Operator Python andmengembalikan nilai False-y pertama, atau nilai terakhir, dan berhenti

Di balik layar

Dalam python, semua angka diartikan sebagai Truekecuali 0. Oleh karena itu, mengatakan:

0 and 10 

sama dengan:

False and True

Yang jelas False. Oleh karena itu logis bahwa ia mengembalikan 0

scharette
sumber
0

Iya. Ini adalah perilaku dan perbandingan yang benar.

Setidaknya dalam Python, A and Bpengembalian Bjika Apada dasarnya Truetermasuk jika ATIDAK Null, TIDAK NoneTIDAK wadah kosong (seperti kosong list, dict, dll). Adikembalikan IFF Apada dasarnya Falseatau Noneatau Kosong atau Null.

Di sisi lain, A or Bpengembalian Ajika Apada dasarnya Truetermasuk jika ATIDAK Null, TIDAK NoneTIDAK wadah kosong (seperti kosong list, dict, dll), jika tidak maka kembali B.

Sangat mudah untuk tidak memperhatikan (atau mengabaikan) perilaku ini karena, dalam Python, setiap objek non-nulltidak kosong yang dievaluasi ke True diperlakukan seperti boolean.

Misalnya, semua yang berikut akan mencetak "Benar"

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

Di sisi lain, semua yang berikut akan mencetak "False"

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"
emmanuelsa
sumber
Terima kasih. Saya ingin menulis Apada dasarnya True. Diperbaiki.
emmanuelsa
0

untuk memahami dengan cara yang sederhana,

DAN: if first_val is False return first_val else second_value

misalnya:

1 and 2 # here it will return 2 because 1 is not False

tapi,

0 and 2 # will return 0 because first value is 0 i.e False

dan => jika ada yang salah, itu akan menjadi salah. jika keduanya benar maka hanya itu yang akan menjadi kenyataan

ATAU : if first_val is False return second_val else first_value

alasannya adalah, jika yang pertama salah maka periksa apakah 2 benar atau tidak.

misalnya:

1 or 2 # here it will return 1 because 1 is not False

tapi,

0 or 2 # will return 2 because first value is 0 i.e False

atau => jika ada yang salah, itu akan benar. jadi jika nilai pertama salah tidak peduli berapa nilai 2 yang seharusnya. sehingga mengembalikan nilai kedua apa pun yang bisa.

jika ada yang benar maka itu akan menjadi kenyataan. jika keduanya salah maka itu akan menjadi salah.

Mohideen bin Mohammed
sumber