Bir durumda görev alabilir miyiz?


92

Bir koşulda görevlendirme yapmak mümkün mü?

Örn.

if (a=some_func()):
    # Use a

İki sorunun aynı cevaba sahip olması, birbirinin kopyası olduğu anlamına gelmez. Kendinize cevap vermek önemsizdir, ancak mantığı veya etrafta bir yol olup olmadığını görmek önemsiz değildir.
mehmet

Yanıtlar:


111

Neden denemiyorsunuz?

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

Yani hayır.

Güncelleme: Bu, Python 3.8'de mümkündür (farklı sözdizimi ile)


35
iyiliksever python diktatörü Guido onları gereksiz ve yararlı olmaktan çok kafa karıştırıcı bulduğu için bu kasıtlı olarak yasaklanmıştır. Arttırma sonrası veya artırma öncesi operatörler (++) olmamasının nedeni aynıdır.
Matt Boehm

4
o eklenmesine izin yaptılar genişletilmiş atama seçeneğinin çünkü 2.0 x = x + 1iken ek arama süresi gerektirir x += 1biraz daha hızlı, ama eminim o yaptı bile yapıyor gibi o kadar. :-)
wescpy

Umarım bu yakında mümkün olur. Python yavaş yavaş gelişiyor
Nik O'Lai

4
"neden denemiyorsunuz" - Çünkü sözdiziminin ne olabileceğini kim bilebilir? Belki OP bunu denedi ve işe yaramadı, ancak bu sözdiziminin farklı olmadığı veya bunu yapmanın amaçlanmayan bir yolu olmadığı anlamına gelmez
Levi H

57

GÜNCELLEME - Orijinal yanıt en alta yakın

Python 3.8, PEP572'yi getirecek

Özet
Bu, NAME: = ifade gösterimini kullanarak bir ifade içindeki değişkenlere atamanın bir yolunu oluşturmak için bir öneridir. Yeni bir istisna olan TargetScopeError eklenir ve değerlendirme sırasında bir değişiklik yapılır.

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

"PEP 572 karmaşası", yaşam için hayırsever diktatör (BDFL) Guido van Rossum tarafından yönetilen 2018 Python Dil Zirvesi oturumunun konusuydu. PEP 572, dile atama ifadeleri (veya "satır içi atamalar") eklemeyi amaçlamaktadır, ancak python-dev posta listesindeki çok sayıda büyük iş parçacığı üzerinde uzun bir tartışma gördü - python fikirleri üzerine birçok turdan sonra bile. Bu konular genellikle tartışmalıydı ve birçoğunun muhtemelen onları ayarladığı noktaya kadar hacimliydi. Zirvede Van Rossum, kabul etmeye meyilli gibi göründüğü özellik önerisinin bir özetini verdi, ancak gelecekte bu tür bir iplik patlamasından nasıl kaçınılacağını tartışmak istedi.

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

Python standart kitaplığından örnekler

site.py env_base sadece bu satırlarda kullanılır ve if atamasını bloğun "başlığı" olarak taşır.

Akım:

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

Gelişmiş:

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

İç içe geçmekten kaçının ve bir girinti seviyesini kaldırın.

Akım:

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

Gelişmiş:

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

copy.py Kod daha düzenli görünüyor ve eğer birden çok iç içe geçmiş durumdan kaçının. (Bu örneğin kökeni için Ek A'ya bakın.)

Akım:

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)

Gelişmiş:

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 yalnızca s + = tz için kullanılır, atamasını if içinde hareket ettirerek kapsamını gösterir.

Akım:

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

Gelişmiş:

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

sysconfig.py while koşulunda fp.readline () öğesini çağırmak ve if satırlarında .match () öğesini çağırmak kodu olmadan daha kompakt hale getirir

anlaşılmasını zorlaştırıyor.

Akım:

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

Gelişmiş:

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

Liste anlamalarını basitleştirme Bir liste anlama, koşulu yakalayarak verimli bir şekilde eşleyebilir ve filtreleyebilir:

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

Benzer şekilde, bir alt ifade, ilk kullanımda ona bir ad verilerek ana ifade içinde yeniden kullanılabilir:

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

Her iki durumda da y değişkeninin kapsama alanına bağlı olduğuna dikkat edin (yani sonuçlar veya malzemelerle aynı seviyede).

Koşul değerlerini yakalama Atama ifadeleri, if veya while ifadesinin başlığında iyi bir etki için kullanılabilir:

# 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)

Özellikle while döngüsü ile bu, sonsuz bir döngü, bir atama ve bir koşula sahip olma ihtiyacını ortadan kaldırabilir. Ayrıca, koşulu olarak basitçe bir işlev çağrısı kullanan bir döngü ile bunu koşulu olarak kullanan ancak aynı zamanda gerçek değeri kullanan bir döngü arasında pürüzsüz bir paralel oluşturur.

Fork Düşük seviyeli UNIX dünyasından bir örnek:

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

Orijinal cevap

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

Python'da, C'den farklı olarak, ifadelerin içinde atama yapılamayacağını unutmayın. C programcıları bundan şikayet edebilir, ancak C programlarında karşılaşılan ortak bir sorun sınıfından kaçınır: == amaçlandığında bir ifadeye = yazmak.

ayrıca bakınız:

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


Bu cevap gibi aslında işaret çünkü neden böyle bir "özellik" kasten Python dışına bırakılmış olabilir. Yeni başlayanlara programlamayı öğretirken, birçok if (foo = 'bar')kişinin değerini test etmek isterken bu hatayı yaptığını gördüm foo.
Jonathan Cross

2
@JonathanCross, Bu "özellik" aslında 3.8'de eklenecek. Olması gerektiği kadar idareli kullanılması pek olası değil - ama en azından sade değil=
John La Rooy

@JohnLaRooy: Örneklere baktığımda, "olması gerektiği kadar idareli kullanılması pek mümkün değil" diye düşünüyorum; ~ 10 örnekten sadece ikisinin kodu gerçekten iyileştirdiğini buldum. (Yani, ya bir süre koşulundaki yegane ifade olarak, çizgiyi tekrarlamaktan ya da vücutta döngü koşuluna sahip olmaktan kaçınmak için ya da yuvalanmayı önlemek için bir elif zincirinde)
Aleksi Torhamo

39

Hayır, BDFL bu özelliği beğenmedi.

Guido van Rossum, oturduğum yerden, "Yaşam İçin Hayırsever Diktatör", Python'u olabildiğince basit tutmak için çok mücadele etti. Verdiği kararların bazılarıyla tartışabiliriz - Ben onun "Hayır" demesini tercih ederdim Ancak Python'u tasarlayan bir komitenin olmadığı gerçeği, bunun yerine büyük ölçüde liyakate dayalı, bir tasarımcının duyarlılıklarını filtreleyen güvenilir bir "danışma kurulu" olması, harika bir dil, IMHO üretti.


15
Basit? Bu özellik kodumun bir kısmını basitleştirebilir çünkü onu daha kompakt ve dolayısıyla daha okunabilir hale getirebilirdi. Şimdi ihtiyacım olan iki hatta ihtiyacım var. Python'un diğer programlama dillerinin yıllardır sahip olduğu özellikleri (ve genellikle çok iyi bir nedenden dolayı) neden reddettiğini asla anlamadım. Özellikle burada bahsettiğimiz bu özellik çok çok kullanışlı.
Regis

6
Daha az kod her zaman daha basit veya daha okunaklı değildir. Örneğin özyinelemeli bir işlevi ele alalım. Döngü eşdeğeri genellikle daha okunabilirdir.
FMF

1
C versiyonunu sevmiyorum, ama if letbir if elif zincirim olduğunda, pas gibi bir şeye sahip olmayı gerçekten özlüyorum , ancak her durumda koşulun değerini saklamam ve kullanmam gerekiyor.
Thayne

1
Şimdi yazdığım kodun (bu konuyu araştırmamın nedeni) bu özellik olmadan ÇOK daha çirkin olduğunu söylemeliyim. İf ve ardından çok sayıda ifs kullanmak yerine, eğer sonuncunun altındaysa bir sonrakini girintilemeye devam etmeliyim.
MikeKulls

17

Bu eski tarifime göre doğrudan değil - ama tarifin dediği gibi semantik eşdeğeri oluşturmak kolaydır, örneğin doğrudan C kodlu bir referans algoritmasından transliterasyon yapmanız gerekiyorsa (daha deyimsel Python'a yeniden düzenleme yapmadan önce tabii ki; -). Yani:

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

Özel durumunuz için çok deyimsel bir Pythonic formu olan BTW, yanlış bir değer döndürdüğünde tam olarak hangi yanlış değerin dönebileceğini biliyorsanız somefunc(örneğin 0),

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

yani bu özel durumda yeniden düzenleme oldukça kolay olacaktır ;-).

Dönüş olabilsem herhangi falsish değerin tür (0, None, '', ...), bir ihtimal:

import itertools

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

ancak basit bir özel oluşturucu tercih edebilirsiniz:

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

for a in getwhile(somefunc):
    # use a

Yapabilseydim bunu iki kez oylardım. Böyle bir şeye gerçekten ihtiyaç duyulduğunda bu harika bir çözüm. Çözümünüzü bir regex Matcher sınıfına uyarladım, bu bir kez başlatıldı ve ardından if ifadesinde .check () ve varsa, eşleşmeyi almak için gövdesi içinde .result () kullanıldı. Teşekkürler! :)
Teekin

16

Evet, ancak yalnızca Python 3.8 ve sonrasında.

PEP 572, Atama İfadeleri önerir ve zaten kabul edilmiştir.

Aktaran dizimi ve semantik PEP parçası:

# 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]

Özel durumunuzda yazabileceksiniz

if a := some_func():
    # Use a

5

Hayır. Python'da atama bir ifade değil, bir ifadedir.


Ve Guido buna başka türlü sahip olamazdı.
Mark Ransom

1
@MarkRansom Guido'ya selam olsun. Doğru .. iç çek.
StephenBoesch

@javadba adam, yanlış olduğundan çok daha fazla haklıydı. Vizyondan sorumlu tek bir kişinin olmasının, komite tarafından tasarlanmasından çok daha tutarlı bir stratejiyle sonuçlandığını takdir ediyorum; Temel ekmek ve tereyağım olan C ++ ile karşılaştırabilir ve karşılaştırabilirim.
Mark Ransom

Hem ruby ​​hem de scala'nın (v farklı diller) python'dan çok daha fazla doğru anladığını hissediyorum: ama her durumda burası değil ..
StephenBoesch

4

Sayesinde Python 3.8 yeni özellik onu kullanmıyor olsa, bu sürümden böyle bir şey yapmak mümkün olacaktır =fakat atama operatörü Ada benzeri :=. Dokümanlardan örnek:

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

2

Atamayı sizin yerinize yapacak bir işlev tanımlayabilirsiniz:

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)

1

Koşullarda atamaların yasa dışı olmasının nedenlerinden biri, hata yapmanın ve Doğru veya Yanlış atamanın daha kolay olmasıdır:

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

Python 3'te True ve False anahtar kelimelerdir, bu yüzden artık risk yok.


1
İçinde [161]: l_empty == [] Çıkış [161]: Gerçek Giriş [162]: [] == [] Çıkış [162]: Doğru Nedenin bu olduğunu sanmıyorum
yanardağ

Çoğu insanın == Trueyine de doğru tarafa geçtiğinden eminim .
numbermaniac

1

Gayri resmi olarak deniz aygırı operatörü olarak da bilinen atama operatörü, 28 Şubat 2018'de PEP572'de oluşturuldu .

Eksiksizlik adına, 3.7 ile 3.8 arasındaki farkları karşılaştırabilmeniz için ilgili bölümleri yayınlayacağım:

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)*
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.