Bisakah kita mendapat tugas dalam kondisi?

92

Apakah mungkin memiliki penugasan dalam kondisi tertentu?

Misalnya.

if (a=some_func()):
    # Use a
Vishal
sumber
Bahwa dua pertanyaan memiliki jawaban yang sama tidak berarti bahwa keduanya merupakan duplikat. Sepele menjawab diri sendiri, tapi tidak sepele melihat alasannya, atau kalau ada jalan keluarnya.
mehmet

Jawaban:

111

Mengapa tidak mencobanya?

>>> def some_func():
...   return 2
... 
>>> a = 2
>>> if (a = some_func()):
  File "<stdin>", line 1
    if (a = some_func()):
          ^
SyntaxError: invalid syntax
>>> 

Jadi tidak.

Pembaruan: Ini dimungkinkan (dengan sintaks berbeda) dengan Python 3.8

Jason Hall
sumber
35
ini sengaja dilarang karena Guido, diktator python yang baik hati, menganggapnya tidak perlu dan lebih membingungkan daripada berguna. Itu alasan yang sama tidak ada operator pasca-kenaikan atau pra-kenaikan (++).
Matt Boehm
4
dia mengizinkan penambahan tugas tambahan di 2.0 karena x = x + 1membutuhkan waktu pencarian tambahan sementara x += 1itu agak lebih cepat, tapi saya yakin dia bahkan tidak suka melakukan itu . :-)
wescpy
berharap, ini akan segera menjadi mungkin. Python lambat berkembang
Nik O'Lai
4
"mengapa tidak mencobanya" - Karena siapa yang tahu apa sintaksnya? Mungkin OP mencobanya dan tidak berhasil, tetapi itu tidak berarti sintaksnya tidak berbeda, atau tidak ada cara untuk melakukannya yang tidak dimaksudkan
Levi H
57

UPDATE - Jawaban asli ada di dekat bagian bawah

Python 3.8 akan membawa PEP572

Abstrak
Ini adalah proposal untuk membuat cara untuk menetapkan variabel dalam ekspresi menggunakan notasi NAME: = expr. Pengecualian baru, TargetScopeError ditambahkan, dan ada satu perubahan pada urutan evaluasi.

https://lwn.net/Articles/757713/

"Kekacauan PEP 572" adalah topik sesi KTT Bahasa Python 2018 yang dipimpin oleh diktator seumur hidup yang baik hati (BDFL) Guido van Rossum. PEP 572 berupaya untuk menambahkan ekspresi tugas (atau "tugas sebaris") ke bahasa, tetapi telah terjadi diskusi yang berkepanjangan atas beberapa utas besar di milis python-dev — bahkan setelah beberapa putaran pada ide-python. Utas-utas itu sering kali menimbulkan perdebatan dan jelas-jelas sangat banyak sampai-sampai banyak yang mungkin baru saja mengabaikannya. Di KTT tersebut, Van Rossum memberikan gambaran umum tentang proposal fitur, yang tampaknya cenderung dia terima, tetapi dia juga ingin mendiskusikan cara menghindari ledakan benang semacam ini di masa mendatang.

https://www.python.org/dev/peps/pep-0572/#examples-from-the-python-standard-library

Contoh dari pustaka standar Python

site.py env_base hanya digunakan pada baris-baris ini, menempatkan tugasnya pada if memindahkannya sebagai "tajuk" dari blok tersebut.

Arus:

env_base = os.environ.get("PYTHONUSERBASE", None)
if env_base:
    return env_base

Peningkatan:

if env_base := os.environ.get("PYTHONUSERBASE", None):
    return env_base
_pydecimal.py

Hindari bersarang if dan hapus satu tingkat indentasi.

Arus:

if self._is_special:
    ans = self._check_nans(context=context)
    if ans:
        return ans

Peningkatan:

if self._is_special and (ans := self._check_nans(context=context)):
    return ans

copy.py Kode terlihat lebih teratur dan menghindari multiple nested if. (Lihat Lampiran A untuk asal contoh ini.)

Arus:

reductor = dispatch_table.get(cls)
if reductor:
    rv = reductor(x)
else:
    reductor = getattr(x, "__reduce_ex__", None)
    if reductor:
        rv = reductor(4)
    else:
        reductor = getattr(x, "__reduce__", None)
        if reductor:
            rv = reductor()
        else:
            raise Error(
                "un(deep)copyable object of type %s" % cls)

Peningkatan:

if reductor := dispatch_table.get(cls):
    rv = reductor(x)
elif reductor := getattr(x, "__reduce_ex__", None):
    rv = reductor(4)
elif reductor := getattr(x, "__reduce__", None):
    rv = reductor()
else:
    raise Error("un(deep)copyable object of type %s" % cls)
datetime.py

tz hanya digunakan untuk s + = tz, memindahkan tugasnya ke dalam if membantu untuk menunjukkan ruang lingkupnya.

Arus:

s = _format_time(self._hour, self._minute,
                 self._second, self._microsecond,
                 timespec)
tz = self._tzstr()
if tz:
    s += tz
return s

Peningkatan:

s = _format_time(self._hour, self._minute,
                 self._second, self._microsecond,
                 timespec)
if tz := self._tzstr():
    s += tz
return s

sysconfig.py Memanggil fp.readline () dalam kondisi while dan memanggil .match () pada baris if membuat kode lebih kompak tanpa

membuatnya lebih sulit untuk dipahami.

Arus:

while True:
    line = fp.readline()
    if not line:
        break
    m = define_rx.match(line)
    if m:
        n, v = m.group(1, 2)
        try:
            v = int(v)
        except ValueError:
            pass
        vars[n] = v
    else:
        m = undef_rx.match(line)
        if m:
            vars[m.group(1)] = 0

Peningkatan:

while line := fp.readline():
    if m := define_rx.match(line):
        n, v = m.group(1, 2)
        try:
            v = int(v)
        except ValueError:
            pass
        vars[n] = v
    elif m := undef_rx.match(line):
        vars[m.group(1)] = 0

Menyederhanakan pemahaman daftar Pemahaman daftar dapat memetakan dan memfilter secara efisien dengan menangkap kondisi:

results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]

Demikian pula, subekspresi dapat digunakan kembali dalam ekspresi utama, dengan memberinya nama pada penggunaan pertama:

stuff = [[y := f(x), x/y] for x in range(5)]

Perhatikan bahwa dalam kedua kasus variabel y terikat dalam ruang lingkup (yaitu pada tingkat yang sama sebagai hasil atau barang).

Menangkap nilai kondisi Ekspresi tugas dapat digunakan untuk memberikan efek yang baik di header pernyataan if atau while:

# Loop-and-a-half
while (command := input("> ")) != "quit":
    print("You entered:", command)

# Capturing regular expression match objects
# See, for instance, Lib/pydoc.py, which uses a multiline spelling
# of this effect
if match := re.search(pat, text):
    print("Found:", match.group(0))
# The same syntax chains nicely into 'elif' statements, unlike the
# equivalent using assignment statements.
elif match := re.search(otherpat, text):
    print("Alternate found:", match.group(0))
elif match := re.search(third, text):
    print("Fallback found:", match.group(0))

# Reading socket data until an empty string is returned
while data := sock.recv(8192):
    print("Received data:", data)

Khususnya dengan while loop, ini bisa menghilangkan kebutuhan untuk memiliki loop tak terbatas, tugas, dan kondisi. Ini juga membuat paralel mulus antara loop yang hanya menggunakan panggilan fungsi sebagai kondisinya, dan yang menggunakannya sebagai kondisinya tetapi juga menggunakan nilai sebenarnya.

Garpu Contoh dari dunia UNIX level rendah:

if pid := os.fork():
    # Parent code
else:
    # Child code

Jawaban asli

http://docs.python.org/tutorial/datastructures.html

Perhatikan bahwa di Python, tidak seperti C, penugasan tidak dapat terjadi di dalam ekspresi. Pemrogram C mungkin mengeluh tentang ini, tetapi ini menghindari kelas umum masalah yang dihadapi dalam program C: mengetik = dalam ekspresi saat == dimaksudkan.

lihat juga:

http://effbot.org/pyfaq/why-can-ti-use-an-assignment-in-an-expression.htm

John La Rooy
sumber
Saya menyukai jawaban ini karena sebenarnya menunjukkan mengapa "fitur" seperti itu mungkin sengaja ditinggalkan dari Python. Saat mengajar pemrograman pemula, saya telah melihat banyak yang membuat kesalahan ini if (foo = 'bar')saat bermaksud menguji nilai foo.
Jonathan Cross
2
@JonathanCross, "Fitur" ini sebenarnya akan ditambahkan di 3.8. Ini tidak mungkin digunakan sesedikit yang seharusnya - tapi setidaknya tidak biasa=
John La Rooy
@JohnLaRooy: Melihat contoh, saya pikir "tidak mungkin digunakan sesering yang seharusnya" sangat tepat; Dari ~ 10 contoh, saya menemukan bahwa hanya dua yang benar-benar meningkatkan kode. (Yaitu, sebagai satu-satunya ekspresi baik dalam kondisi sementara, untuk menghindari duplikasi garis atau kondisi loop di tubuh, atau dalam rantai elif untuk menghindari bersarang)
Aleksi Torhamo
39

Tidak, BDFL tidak menyukai fitur itu.

Dari tempat saya duduk, Guido van Rossum, "Diktator Baik untuk Kehidupan”, telah berjuang keras untuk menjaga agar Python tetap sesederhana mungkin. Kita dapat berdalih dengan beberapa keputusan yang dia buat - saya lebih suka dia berkata 'Tidak 'lebih sering. Tetapi fakta bahwa belum ada komite yang merancang Python, melainkan "dewan penasihat" tepercaya, yang sebagian besar didasarkan pada prestasi, menyaring melalui kepekaan seorang desainer, telah menghasilkan bahasa yang sangat bagus, IMHO.

Kevin Little
sumber
15
Sederhana? Fitur ini dapat menyederhanakan beberapa kode saya karena dapat membuatnya lebih kompak dan lebih mudah dibaca. Sekarang saya perlu dua baris yang dulu saya butuhkan. Saya tidak pernah mengerti mengapa Python menolak fitur yang dimiliki bahasa pemrograman lain selama bertahun-tahun (dan seringkali karena alasan yang sangat bagus). Terutama fitur yang kita bicarakan di sini sangat, sangat berguna.
Regis Mei
6
Lebih sedikit kode tidak selalu lebih sederhana atau lebih mudah dibaca. Ambil fungsi rekursif misalnya. Ini setara loop seringkali lebih mudah dibaca.
FMF
1
Saya tidak suka versi C-nya, tetapi saya sangat merindukan memiliki sesuatu seperti karat if letketika saya memiliki rantai if elif, tetapi perlu menyimpan dan menggunakan nilai kondisi di setiap kasus.
Thayne
1
Saya harus mengatakan bahwa kode yang saya tulis sekarang (alasan saya mencari masalah ini) JAUH lebih jelek tanpa fitur ini. Alih-alih menggunakan if diikuti oleh banyak ifs lainnya, saya harus terus mengindentasi if under the last else.
MikeKulls
17

Tidak secara langsung, sesuai resep lama saya ini - tetapi seperti yang dikatakan resep itu mudah untuk membangun padanan semantik, misalnya jika Anda perlu mentransliterasi langsung dari algoritme referensi berkode-C (sebelum melakukan refactoring ke Python yang lebih idiomatik, tentu saja; -). Yaitu:

class DataHolder(object):
    def __init__(self, value=None): self.value = value
    def set(self, value): self.value = value; return value
    def get(self): return self.value

data = DataHolder()

while data.set(somefunc()):
  a = data.get()
  # use a

BTW, bentuk Pythonic yang sangat idiomatik untuk kasus spesifik Anda, jika Anda tahu persis apa nilai palsu yang somefuncdapat dikembalikan ketika mengembalikan nilai yang salah (misalnya 0), adalah

for a in iter(somefunc, 0):
  # use a

jadi dalam kasus khusus ini refactoring akan sangat mudah ;-).

Jika pengembalian bisa menjadi salah jenis nilai falsish (0, None, '', ...), salah satu kemungkinan adalah:

import itertools

for a in itertools.takewhile(lambda x: x, iter(somefunc, object())):
    # use a

tetapi Anda mungkin lebih suka generator khusus sederhana:

def getwhile(func, *a, **k):
    while True:
      x = func(*a, **k)
      if not x: break
      yield x

for a in getwhile(somefunc):
    # use a
Alex Martelli
sumber
Saya akan memilih ini dua kali jika saya bisa. Ini adalah solusi yang bagus untuk saat-saat ketika sesuatu seperti ini benar-benar dibutuhkan. Saya mengadaptasi solusi Anda ke kelas Matcher regex, yang dibuat sekali dan kemudian .check () digunakan dalam pernyataan if dan .result () digunakan di dalam tubuhnya untuk mengambil kecocokan, jika ada. Terima kasih! :)
Teekin
16

Ya, tetapi hanya dari Python 3.8 dan seterusnya.

PEP 572 mengusulkan Ekspresi Tugas dan telah diterima.

Mengutip bagian Sintaks dan semantik dari PEP:

# Handle a matched regex
if (match := pattern.search(data)) is not None:
    # Do something with match

# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
   process(chunk)

# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]

# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]

Dalam kasus khusus Anda, Anda akan bisa menulis

if a := some_func():
    # Use a
timgeb
sumber
5

Tidak. Tugas dengan Python adalah pernyataan, bukan ekspresi.

Ignacio Vazquez-Abrams
sumber
Dan Guido tidak akan melakukannya dengan cara lain.
Mark Ransom
1
@ MarkRans Semua hail Guido. Benar .. menghela napas.
StephenBoesch
@javadba pria itu lebih sering benar daripada dia salah. Saya menghargai bahwa memiliki satu orang yang bertanggung jawab atas visi menghasilkan strategi yang jauh lebih koheren daripada desain oleh komite; Saya dapat membandingkan dan membedakan dengan C ++ yang merupakan roti dan mentega utama saya.
Mark Ransom
Saya merasa baik ruby ​​dan scala (v bahasa yang berbeda) melakukannya dengan benar secara signifikan lebih dari python: tetapi bagaimanapun juga di sini bukanlah tempatnya ..
StephenBoesch
4

Berkat fitur baru Python 3.8, Anda dapat melakukan hal seperti itu dari versi ini, meskipun tidak menggunakan =tetapi operator penugasan seperti Ada :=. Contoh dari dokumen:

# Handle a matched regex
if (match := pattern.search(data)) is not None:
    # Do something with match
Jean-François Fabre
sumber
2

Anda dapat menentukan fungsi untuk melakukan penugasan untuk Anda:

def assign(name, value):
    import inspect
    frame = inspect.currentframe()
    try:
        locals_ = frame.f_back.f_locals
    finally:
        del frame 
    locals_[name] = value
    return value

if assign('test', 0):
    print("first", test)
elif assign('xyz', 123):
    print("second", xyz)
Willem Hengeveld
sumber
1

Salah satu alasan mengapa penetapan ilegal dalam kondisi adalah karena lebih mudah membuat kesalahan dan menetapkan Benar atau Salah:

some_variable = 5

# This does not work
# if True = some_variable:
#   do_something()

# This only works in Python 2.x
True = some_variable

print True  # returns 5

Di Python 3 True dan False adalah kata kunci, jadi tidak ada resiko lagi.

pengguna2979916
sumber
1
Masuk [161]: l_empty == [] Keluar [161]: Benar Masuk [162]: [] == [] Keluar [162]: Benar Saya tidak berpikir itu alasannya
gunung berapi
Cukup yakin kebanyakan orang meletakkannya == Truedi sisi kanan.
numbermaniac
1

Operator penugasan - juga dikenal secara informal sebagai operator walrus - dibuat pada 28-Feb-2018 di PEP572 .

Demi kelengkapan, saya akan memposting bagian-bagian yang relevan sehingga Anda dapat membandingkan perbedaan antara 3.7 dan 3.8:

3.7
---
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
test: or_test ['if' or_test 'else' test] | lambdef
test_nocond: or_test | lambdef_nocond
lambdef: 'lambda' [varargslist] ':' test
lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*

3.8
---
if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
namedexpr_test: test [':=' test]                         <---- WALRUS OPERATOR!!!
test: or_test ['if' or_test 'else' test] | lambdef
or_test: and_test ('or' and_test)*
and_test: not_test ('and' not_test)*
not_test: 'not' not_test | comparison
comparison: expr (comp_op expr)*
BPL
sumber