Chúng ta có thể có nhiệm vụ trong một điều kiện?


92

Có thể có sự phân công trong một điều kiện?

Đối với người yêu cũ.

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

Hai câu hỏi có cùng câu trả lời không có nghĩa là chúng trùng lặp. Tầm thường để tự trả lời, nhưng không tầm thường để xem lý do, hoặc nếu có một cách xung quanh.
mehmet

Câu trả lời:


111

Tại sao không thử nó ra?

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

Vì vậy, không.

Cập nhật: Điều này có thể (với cú pháp khác) trong Python 3.8


35
điều này bị cấm cố ý vì Guido, nhà độc tài trăn nhân từ, thấy chúng không cần thiết và khó hiểu hơn là hữu ích. Đó cũng là lý do không có toán tử tăng sau hoặc tăng trước (++).
Matt Boehm

4
anh ấy đã cho phép bổ sung phép gán tăng cường trong 2.0 vì x = x + 1yêu cầu thời gian tra cứu bổ sung trong khi x += 1nhanh hơn một chút, nhưng tôi chắc chắn rằng anh ấy thậm chí không thích làm điều đó nhiều. :-)
wescpy

hy vọng, điều này sẽ sớm trở thành khả thi. Python chậm phát triển
Nik O'Lai

4
"tại sao không thử" - Bởi vì ai biết cú pháp có thể là gì? Có thể OP đã thử điều đó và nó không hoạt động, nhưng điều đó không có nghĩa là cú pháp không khác hoặc không có cách nào để làm điều đó không theo ý muốn
Levi H

57

CẬP NHẬT - Câu trả lời gốc ở gần cuối

Python 3.8 sẽ mang đến PEP572

Tóm tắt
Đây là một đề xuất để tạo một cách để gán cho các biến trong một biểu thức bằng cách sử dụng ký hiệu NAME: = expr. Một ngoại lệ mới, TargetScopeError được thêm vào và có một thay đổi đối với thứ tự đánh giá.

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

"Vụ lộn xộn PEP 572" là chủ đề của phiên họp Hội nghị thượng đỉnh về ngôn ngữ Python năm 2018 do nhà độc tài nhân từ vì sự sống (BDFL) Guido van Rossum dẫn đầu. PEP 572 tìm cách thêm biểu thức gán (hoặc "phép gán nội tuyến") vào ngôn ngữ, nhưng nó đã chứng kiến ​​một cuộc thảo luận kéo dài về nhiều chủ đề lớn trong danh sách gửi thư python-dev — ngay cả sau nhiều vòng về ý tưởng python. Những chủ đề đó thường gây tranh cãi và rõ ràng là đồ sộ đến mức nhiều người có thể chỉ chỉnh chúng ra. Tại hội nghị thượng đỉnh, Van Rossum đã đưa ra một cái nhìn tổng quan về đề xuất tính năng mà ông có vẻ có xu hướng chấp nhận, nhưng ông cũng muốn thảo luận về cách tránh sự bùng nổ chủ đề này trong tương lai.

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

Ví dụ từ thư viện chuẩn Python

site.py env_base chỉ được sử dụng trên những dòng này, việc gán nó vào if sẽ di chuyển nó làm "tiêu đề" của khối.

Hiện hành:

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

Cải thiện:

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

Tránh lồng ghép nếu và loại bỏ một mức thụt lề.

Hiện hành:

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

Cải thiện:

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

copy.py Mã trông đều đặn hơn và tránh nhiều nếu. (Xem Phụ lục A về nguồn gốc của ví dụ này.)

Hiện hành:

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)

Cải thiện:

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 chỉ được sử dụng cho s + = tz, việc di chuyển phép gán của nó vào bên trong if giúp hiển thị phạm vi của nó.

Hiện hành:

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

Cải thiện:

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

sysconfig.py Gọi fp.readline () trong điều kiện while và gọi .match () trên các dòng if làm cho mã gọn gàng hơn mà không cần

làm cho nó khó hiểu hơn.

Hiện hành:

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

Cải thiện:

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

Đơn giản hóa việc hiểu danh sách Việc hiểu danh sách có thể lập bản đồ và lọc hiệu quả bằng cách nắm bắt điều kiện:

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

Tương tự, một biểu thức con có thể được sử dụng lại trong biểu thức chính, bằng cách đặt tên cho nó trong lần sử dụng đầu tiên:

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

Lưu ý rằng trong cả hai trường hợp, biến y bị ràng buộc trong phạm vi chứa (nghĩa là ở cùng cấp với kết quả hoặc nội dung).

Thu thập giá trị điều kiện Biểu thức gán có thể được sử dụng để tạo hiệu quả tốt trong tiêu đề của câu lệnh if hoặc 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)

Đặc biệt với vòng lặp while, điều này có thể loại bỏ sự cần thiết phải có một vòng lặp vô hạn, một phép gán và một điều kiện. Nó cũng tạo ra một sự song song mượt mà giữa một vòng lặp chỉ sử dụng một lệnh gọi hàm làm điều kiện của nó và một vòng lặp sử dụng điều kiện đó làm điều kiện nhưng cũng sử dụng giá trị thực tế.

Fork Một ví dụ từ thế giới UNIX cấp thấp:

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

Câu trả lời ban đầu

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

Lưu ý rằng trong Python, không giống như C, phép gán không thể xảy ra bên trong các biểu thức. Các lập trình viên C có thể càu nhàu về điều này, nhưng nó tránh được một lớp vấn đề phổ biến gặp phải trong các chương trình C: gõ = trong một biểu thức khi có ý định dùng ==.

cũng thấy:

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


Tôi thích câu trả lời này vì nó thực sự chỉ ra lý do tại sao một "tính năng" như vậy có thể đã bị Python cố tình loại bỏ. Khi dạy lập trình cho người mới bắt đầu, tôi đã thấy nhiều người mắc lỗi này if (foo = 'bar')trong khi định kiểm tra giá trị của foo.
Jonathan Cross

2
@JonathanCross, "Tính năng" này thực sự sẽ được thêm vào trong 3.8. Nó không có khả năng được sử dụng một cách tiết kiệm như bình thường - nhưng ít nhất nó không phải là một loại đơn giản=
John La Rooy

@JohnLaRooy: Nhìn vào các ví dụ, tôi nghĩ rằng "không có khả năng được sử dụng một cách tiết kiệm như nó nên" là đúng; Trong số ~ 10 ví dụ, tôi thấy rằng chỉ có hai ví dụ thực sự cải thiện mã. (Cụ thể, như là một biểu thức duy nhất hoặc trong điều kiện nhất thời, để tránh trùng lặp dòng hoặc có điều kiện vòng lặp trong nội dung hoặc trong một chuỗi elif để tránh lồng vào nhau)
Aleksi Torhamo

39

Không, BDFL không thích tính năng đó.

Từ chỗ tôi ngồi, Guido van Rossum, "Nhà độc tài nhân từ cho cuộc sống", đã chiến đấu hết mình để giữ cho Python đơn giản nhất có thể. Chúng ta có thể phân minh với một số quyết định của anh ấy - Tôi thích anh ấy nói 'Không Nhưng thực tế là không có một ủy ban nào thiết kế Python, mà thay vào đó là một "ban cố vấn" đáng tin cậy, phần lớn dựa trên thành tích, lọc qua sự nhạy cảm của một nhà thiết kế, đã tạo ra một ngôn ngữ tuyệt vời, IMHO.


15
Đơn giản? Tính năng này có thể đơn giản hóa khá nhiều mã của tôi vì nó có thể làm cho nó nhỏ gọn hơn và dễ đọc hơn. Bây giờ tôi cần hai dòng mà tôi đã từng cần một. Tôi chưa bao giờ hiểu tại sao Python lại từ chối các tính năng mà các ngôn ngữ lập trình khác có trong nhiều năm (và thường là vì một lý do rất chính đáng). Đặc biệt là tính năng mà chúng ta đang nói đến ở đây rất, rất hữu ích.
Regis Tháng

6
Mã ít hơn không phải lúc nào cũng đơn giản hoặc dễ đọc. Lấy một hàm đệ quy làm ví dụ. Nó tương đương với vòng lặp thường dễ đọc hơn.
FMF

1
Tôi không thích phiên bản C của nó, nhưng tôi thực sự nhớ có một cái gì đó giống như gỉ if letkhi tôi có chuỗi if elif, nhưng cần phải lưu trữ và sử dụng giá trị của điều kiện trong từng trường hợp.
Thayne

1
Tôi phải nói rằng mã tôi đang viết bây giờ (lý do tôi tìm kiếm vấn đề này) là xấu hơn rất nhiều nếu không có tính năng này. Thay vì sử dụng if theo sau bởi nhiều ifs khác, tôi cần tiếp tục thụt lề cho if tiếp theo dưới if cuối cùng.
MikeKulls

17

Không trực tiếp, theo công thức cũ này của tôi - nhưng như công thức cho biết rất dễ dàng để xây dựng ngữ nghĩa tương đương, ví dụ nếu bạn cần chuyển ngữ trực tiếp từ thuật toán tham chiếu được mã hóa C (tất nhiên là trước khi cấu trúc lại thành Python dễ hiểu hơn; -). I E:

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, một dạng Pythonic rất thành ngữ cho trường hợp cụ thể của bạn, nếu bạn biết chính xác giá trị sai somefunccó thể trả về khi nó trả về giá trị giả (ví dụ 0), là

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

vì vậy trong trường hợp cụ thể này, việc tái cấu trúc sẽ khá dễ dàng ;-).

Nếu sự trở lại có thể là bất kỳ loại giá trị falsish (0, None, '', ...), một trong những khả năng là:

import itertools

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

nhưng bạn có thể thích một trình tạo tùy chỉnh đơn giản:

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

for a in getwhile(somefunc):
    # use a

Tôi sẽ bỏ phiếu hai lần nếu tôi có thể. Đây là một giải pháp tuyệt vời cho những lúc thực sự cần một thứ như thế này. Tôi đã điều chỉnh giải pháp của bạn cho một lớp regex Matcher, lớp này được khởi tạo một lần và sau đó .check () được sử dụng trong câu lệnh if và .result () được sử dụng bên trong phần thân của nó để truy xuất kết quả phù hợp, nếu có. Cảm ơn! :)
Teekin

16

Có, nhưng chỉ từ Python 3.8 trở đi.

PEP 572 đề xuất Biểu thức Bài tập và đã được chấp nhận.

Trích dẫn phần Cú pháp và ngữ nghĩa của 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]

Trong trường hợp cụ thể của bạn, bạn sẽ có thể viết

if a := some_func():
    # Use a

5

Không. Phép gán trong Python là một câu lệnh, không phải một biểu thức.


Và Guido sẽ không có cách nào khác.
Mark Ransom

1
@MarkRansom Tất cả chào mừng Guido. Đúng .. thở dài.
StephenBoesch

@javadba anh chàng đã đúng thường xuyên hơn là sai. Tôi đánh giá cao việc có một người duy nhất chịu trách nhiệm về tầm nhìn dẫn đến một chiến lược chặt chẽ hơn nhiều so với việc thiết kế theo ủy ban; Tôi có thể so sánh và đối chiếu với C ++, là bánh mì và bơ chính của tôi.
Đánh dấu tiền chuộc

Tôi cảm thấy cả ruby và scala (v ngôn ngữ khác nhau) làm cho nó đúng moreso đáng kể so với python: nhưng trong mọi trường hợp ở đây không phải là nơi ..
StephenBoesch

4

Nhờ tính năng mới của Python 3.8, có thể thực hiện điều đó từ phiên bản này, mặc dù không sử dụng =nhưng toán tử gán giống Ada :=. Ví dụ từ tài liệu:

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

2

Bạn có thể xác định một hàm để thực hiện việc gán cho bạn:

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

Một trong những lý do tại sao việc chuyển nhượng là bất hợp pháp trong các điều kiện là bạn dễ mắc lỗi và gán Đúng hoặc Sai:

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

Trong Python 3 True và False là các từ khóa, vì vậy không còn rủi ro nữa.


1
Trong [161]: l_empty == [] Hết [161]: Đúng Trong [162]: [] == [] Ngoài [162]: Đúng, tôi không nghĩ đó là lý do
núi lửa

Khá chắc chắn rằng hầu hết mọi người đều đặt == Trueở bên phải.
numbermaniac

1

Toán tử gán - còn được gọi là toán tử hải mã - được tạo vào ngày 28 tháng 2 năm 2018 trong PEP572 .

Để đầy đủ, tôi sẽ đăng các phần liên quan để bạn có thể so sánh sự khác biệt giữa 3.7 và 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)*
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.