Apakah ada persamaan Python dari operator penggabungan C # null?


301

Dalam C # ada operator penggabungan nol (ditulis sebagai ??) yang memungkinkan untuk memeriksa nol dengan mudah (pendek) selama penugasan:

string s = null;
var other = s ?? "some default value";

Apakah ada yang setara dengan python?

Saya tahu bahwa saya dapat melakukan:

s = None
other = s if s else "some default value"

Tetapi apakah ada cara yang lebih pendek (di mana saya tidak perlu mengulangi s)?


14
The ??operator diusulkan sebagai PEP 505 .
200_success

Jawaban:


421
other = s or "some default value"

Ok, harus diklarifikasi cara kerja oroperator. Ini adalah operator boolean, jadi ia bekerja dalam konteks boolean. Jika nilainya bukan boolean, nilai tersebut dikonversi ke boolean untuk keperluan operator.

Perhatikan bahwa oroperator tidak hanya mengembalikan Trueatau False. Sebagai gantinya, ia mengembalikan operan pertama jika operan pertama bernilai true, dan mengembalikan operan kedua jika operan pertama bernilai false.

Dalam kasus ini, ekspresi x or ykembali xjika itu Trueatau bernilai true ketika dikonversi ke boolean. Kalau tidak, ia kembali y. Untuk sebagian besar kasus, ini akan berfungsi untuk tujuan yang sama dari operator penggabungan nol C♯, tetapi perlu diingat:

42    or "something"    # returns 42
0     or "something"    # returns "something"
None  or "something"    # returns "something"
False or "something"    # returns "something"
""    or "something"    # returns "something"

Jika Anda menggunakan variabel Anda suntuk menyimpan sesuatu yang merupakan referensi ke instance dari kelas atau None(selama kelas Anda tidak mendefinisikan anggota __nonzero__()dan __len__()), aman untuk menggunakan semantik yang sama dengan operator penggabungan nol.

Bahkan, mungkin berguna untuk memiliki efek samping Python ini. Karena Anda tahu nilai apa yang bernilai false, Anda dapat menggunakan ini untuk memicu nilai default tanpa menggunakan Nonesecara spesifik (objek kesalahan, misalnya).

Dalam beberapa bahasa perilaku ini disebut sebagai operator Elvis .


3
Apakah ini akan berfungsi sama? Maksud saya, apakah itu akan pecah jika smerupakan nilai yang valid tetapi tidak benar? (Saya tidak tahu Python, jadi saya tidak yakin apakah konsep 'kebenaran' berlaku.)
cHao

9
Angka 0,, Nonedan wadah kosong (termasuk string) dianggap salah, selain konstanta False. Kebanyakan hal lain dianggap benar. Saya akan mengatakan bahwa bahaya utama di sini adalah bahwa Anda akan mendapatkan nilai yang benar tetapi non-string, tetapi itu tidak akan menjadi masalah dalam beberapa program.
hati

25
Menggunakan yang lain ini akan mendapatkan nilai default jika s adalah None atau False , yang mungkin bukan yang diinginkan.
pafcu

6
Ada banyak bug tidak jelas yang disebabkan oleh ini juga. Misalnya sebelum Python 3.5, datetime.time(0)ternyata juga falsy!
Antti Haapala

19
Ini buruk. Saya sarankan menambahkan pemberitahuan tentang perangkapnya. Dan merekomendasikan untuk tidak menggunakannya.
Mateen Ulhaq

61

Dengan ketat,

other = s if s is not None else "default value"

Kalau tidak, s = Falseakan menjadi "default value", yang mungkin bukan yang dimaksudkan.

Jika Anda ingin mempersingkat ini, cobalah:

def notNone(s,d):
    if s is None:
        return d
    else:
        return s

other = notNone(s, "default value")

1
Consider x()?.y()?.z()
nurettin

40

Berikut adalah fungsi yang akan mengembalikan argumen pertama yang tidak None:

def coalesce(*arg):
  return reduce(lambda x, y: x if x is not None else y, arg)

# Prints "banana"
print coalesce(None, "banana", "phone", None)

reduce()mungkin perlu mengulangi semua argumen meskipun argumen pertama tidak None, jadi Anda juga dapat menggunakan versi ini:

def coalesce(*arg):
  for el in arg:
    if el is not None:
      return el
  return None

23
def coalesce(*arg): return next((a for a in arg if a is not None), None)melakukan hal yang sama seperti contoh terakhir Anda dalam satu baris.
glglgl

2
Saya mendapatkan bahwa orang ingin menjelaskan jika sytnax dll, tetapi bersatu mengambil daftar argumen sewenang-wenang jadi ini benar-benar harus menjadi jawaban teratas.
Eric Twilegar

2
glglgl memiliki jawaban terbaik. Saya menggunakan timeit pada test array yang besar dan implementasi implementasinya lambat, multi-line untuk / jika versi lebih cepat, dan implementasi berikutnya sangat sedikit tertinggal. Versi berikutnya adalah keseluruhan terbaik ketika mempertimbangkan kesederhanaan dan keringkasan.
liat

3
@glglgl memiliki cuplikan yang menarik. Sayangnya karena Python tidak memiliki pass-by-name, menyatu seperti ini bukan hubungan arus pendek; semua argumen dievaluasi sebelum kode berjalan.
user1338062

Consider x()?.y()?.z()
nurettin

5

Saya menyadari ini dijawab, tetapi ada opsi lain ketika Anda berurusan dengan objek.

Jika Anda memiliki objek yang mungkin:

{
   name: {
      first: "John",
      last: "Doe"
   }
}

Kamu bisa memakai:

obj.get(property_name, value_if_null)

Suka:

obj.get("name", {}).get("first", "Name is missing") 

Dengan menambahkan {}sebagai nilai default, jika "nama" hilang, objek kosong dikembalikan dan diteruskan ke get berikutnya. Ini mirip dengan navigasi aman-nol di C #, yang akan seperti obj?.name?.first.


Tidak semua objek memiliki .get, ini hanya berfungsi untuk objek seperti dict
timdiels

Saya juga mengirim edit jawaban untuk dibahas getattr().
dgw

getpada dict tidak menggunakan parameter default jika nilainya Tidak ada tetapi menggunakan parameter default jika nilai tidak ada karena kunci tidak ada dalam dict. {'a': None}.get('a', 'I do not want None')masih akan memberi Anda Nonehasilnya.
Patrick Mevzek

3

Selain jawaban Juliano tentang perilaku "atau": "cepat"

>>> 1 or 5/0
1

Jadi terkadang itu mungkin jalan pintas yang berguna untuk hal-hal seperti

object = getCachedVersion() or getFromDB()

17
Istilah yang Anda cari adalah "korsleting."
jpmc26

1

Selain jawaban @Bothwells (yang saya sukai) untuk nilai-nilai tunggal, untuk mengecek penetapan nilai fungsi kembali, Anda dapat menggunakan walrus-operator baru (karena python3.8):

def test():
    return

a = 2 if (x:= test()) is None else x

Dengan demikian, testfungsi tidak perlu dievaluasi dua kali (seperti pada a = 2 if test() is None else test())


1

Jika Anda perlu membuat sarang lebih dari satu operasi penggabungan nol seperti:

model?.data()?.first()

Ini bukan masalah yang mudah diselesaikan or. Ini juga tidak dapat dipecahkan dengan .get()yang membutuhkan jenis kamus atau sejenisnya (dan tidak dapat disarangkan pula) ataugetattr() yang akan mengeluarkan pengecualian ketika NoneType tidak memiliki atribut.

Pip yang relevan yang mempertimbangkan menambahkan null penggabungan ke bahasa adalah PEP 505 dan diskusi yang relevan dengan dokumen ada di utas python-ideas .


0

Mengenai jawaban oleh @Hugh Bothwell, @mortehu dan @glglgl.

Setup Dataset untuk pengujian

import random

dataset = [random.randint(0,15) if random.random() > .6 else None for i in range(1000)]

Tetapkan implementasi

def not_none(x, y=None):
    if x is None:
        return y
    return x

def coalesce1(*arg):
  return reduce(lambda x, y: x if x is not None else y, arg)

def coalesce2(*args):
    return next((i for i in args if i is not None), None)

Buat fungsi tes

def test_func(dataset, func):
    default = 1
    for i in dataset:
        func(i, default)

Hasil pada mac i7 @ 2.7Ghz menggunakan python 2.7

>>> %timeit test_func(dataset, not_none)
1000 loops, best of 3: 224 µs per loop

>>> %timeit test_func(dataset, coalesce1)
1000 loops, best of 3: 471 µs per loop

>>> %timeit test_func(dataset, coalesce2)
1000 loops, best of 3: 782 µs per loop

Jelas itu not_none fungsi menjawab pertanyaan OP dengan benar dan menangani masalah "falsy". Ini juga yang tercepat dan termudah untuk dibaca. Jika menerapkan logika di banyak tempat, itu jelas cara terbaik untuk pergi.

Jika Anda memiliki masalah di mana Anda ingin menemukan nilai bukan nol pertama di iterable, maka respons @ mortehu adalah cara yang harus dilakukan. Tetapi ini adalah solusi untuk masalah yang berbeda dari OP, meskipun sebagian dapat menangani kasus itu. Itu tidak bisa mengambil nilai ANDER default. Argumen terakhir adalah nilai default yang dikembalikan, tetapi Anda tidak akan meneruskan iterable dalam kasus itu dan juga tidak eksplisit bahwa argumen terakhir adalah nilai default.

Anda kemudian dapat melakukan di bawah ini, tetapi saya masih akan menggunakan not_nulluntuk kasus penggunaan nilai tunggal.

def coalesce(*args, **kwargs):
    default = kwargs.get('default')
    return next((a for a in arg if a is not None), default)

0

Bagi mereka seperti saya yang tersandung di sini mencari solusi yang layak untuk masalah ini, ketika variabel mungkin tidak terdefinisi, yang paling dekat saya dapatkan adalah:

if 'variablename' in globals() and ((variablename or False) == True):
  print('variable exists and it\'s true')
else:
  print('variable doesn\'t exist, or it\'s false')

Perhatikan bahwa string diperlukan saat memeriksa global, tetapi setelah itu variabel aktual digunakan saat memeriksa nilai.

Lebih lanjut tentang keberadaan variabel: Bagaimana cara saya memeriksa apakah ada variabel?


1
(variablename or False) == Truesama denganvariablename == True
mic

-2
Python has a get function that its very useful to return a value of an existent key, if the key exist;
if not it will return a default value.

def main():
    names = ['Jack','Maria','Betsy','James','Jack']
    names_repeated = dict()
    default_value = 0

    for find_name in names:
        names_repeated[find_name] = names_repeated.get(find_name, default_value) + 1

Jika Anda tidak dapat menemukan nama di dalam kamus, itu akan mengembalikan nilai default_v, jika nama ada maka itu akan menambah nilai yang ada dengan 1.

Semoga ini bisa membantu


1
Hai, selamat datang di Stack Overflow. Informasi baru apa yang ditambahkan jawaban Anda yang belum dicakup oleh jawaban yang ada? Lihat jawaban @ Craig misalnya
Gricey

-6

Dua fungsi di bawah ini saya temukan sangat berguna ketika berhadapan dengan banyak kasus pengujian variabel.

def nz(value, none_value, strict=True):
    ''' This function is named after an old VBA function. It returns a default
        value if the passed in value is None. If strict is False it will
        treat an empty string as None as well.

        example:
        x = None
        nz(x,"hello")
        --> "hello"
        nz(x,"")
        --> ""
        y = ""   
        nz(y,"hello")
        --> ""
        nz(y,"hello", False)
        --> "hello" '''

    if value is None and strict:
        return_val = none_value
    elif strict and value is not None:
        return_val = value
    elif not strict and not is_not_null(value):
        return_val = none_value
    else:
        return_val = value
    return return_val 

def is_not_null(value):
    ''' test for None and empty string '''
    return value is not None and len(str(value)) > 0

5
Hal-hal semacam ini menambah sejumlah terminologi yang sedikit berbeda (misalnya "null" dan "nz" yang tidak ada artinya dalam konteks Python), yang diimpor dari bahasa lain, ditambah dengan varian (ketat atau tidak ketat!). Ini hanya menambah kebingungan. Pemeriksaan "is None" eksplisit adalah apa yang harus Anda gunakan. Selain itu, Anda tidak mendapatkan manfaat dari semantik singkat apa pun yang dapat dilakukan operator ketika Anda menggunakan panggilan fungsi.
spookylukey
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.