Có một Python tương đương với toán tử hợp nhất C # null không?


301

Trong C # có một toán tử hợp nhất null (được viết là ??) cho phép kiểm tra null dễ dàng (ngắn) trong khi gán:

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

Có một con trăn tương đương?

Tôi biết rằng tôi có thể làm:

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

Nhưng có cách nào thậm chí ngắn hơn (nơi tôi không cần lặp lại s)?


14
Các ??nhà điều hành được đề xuất như PEP 505 .
200_success

Câu trả lời:


421
other = s or "some default value"

Ok, nó phải được làm rõ or vận hành. Nó là một toán tử boolean, vì vậy nó hoạt động trong bối cảnh boolean. Nếu các giá trị không phải là boolean, chúng được chuyển đổi thành boolean cho các mục đích của toán tử.

Lưu ý rằng ortoán tử không chỉ trả về Truehoặc False. Thay vào đó, nó trả về toán hạng thứ nhất nếu toán hạng thứ nhất ước lượng là true và nó trả về toán hạng thứ hai nếu toán hạng thứ nhất ước lượng thành false.

Trong trường hợp này, biểu thức x or ytrả về xnếu nó là Truehoặc đánh giá là true khi được chuyển đổi thành boolean. Nếu không, nó trở lại y. Trong hầu hết các trường hợp, điều này sẽ phục vụ cho cùng một mục đích của toán tử hợp nhất null của C♯, nhưng hãy ghi nhớ:

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

Nếu bạn sử dụng biến của mình sđể giữ một cái gì đó là tham chiếu đến thể hiện của một lớp hoặc None(miễn là lớp của bạn không xác định thành viên __nonzero__()__len__() ), thì có thể sử dụng cùng một ngữ nghĩa như toán tử hợp nhất null.

Trên thực tế, nó có thể hữu ích khi có tác dụng phụ này của Python. Vì bạn biết giá trị nào đánh giá là sai, bạn có thể sử dụng giá trị này để kích hoạt giá trị mặc định mà không cần sử dụng Nonecụ thể (ví dụ: một đối tượng lỗi).

Trong một số ngôn ngữ, hành vi này được gọi là toán tử Elvis .


3
Điều này sẽ làm việc như nhau? Ý tôi là, nó sẽ phá vỡ nếu slà một giá trị hợp lệ nhưng không phải là sự thật? (Tôi không biết Python, vì vậy tôi không chắc liệu khái niệm 'sự thật' có áp dụng hay không.)
cHao

9
Số 0 Nonevà các thùng chứa rỗng (bao gồm cả chuỗi) được coi là sai, ngoài hằng số False. Hầu hết mọi thứ khác được coi là đúng. Tôi sẽ nói rằng mối nguy hiểm chính ở đây là bạn sẽ có được một giá trị thực nhưng không phải là chuỗi, nhưng đó sẽ không phải là vấn đề trong một số chương trình.
loại

25
Sử dụng cái này khác sẽ nhận được giá trị mặc định nếu s là Không hoặc Sai , có thể không phải là thứ muốn.
pafcu

6
Có rất nhiều lỗi tối nghĩa gây ra bởi điều này là tốt. Ví dụ trước Python 3.5, datetime.time(0)cũng là giả!
Antti Haapala

19
Điều này tệ đây. Tôi khuyên bạn nên thêm một thông báo về những cạm bẫy của nó. Và khuyến cáo không sử dụng nó.
Mateen Ulhaq

61

Nghiêm khắc

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

Nếu không, s = Falsesẽ trở thành "default value", có thể không phải là những gì đã được dự định.

Nếu bạn muốn làm điều này ngắn hơn, hãy thử:

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

Đây là một hàm sẽ trả về đối số đầu tiên không phải là 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()có thể lặp lại một cách không cần thiết trên tất cả các đối số ngay cả khi đối số đầu tiên không có None, vì vậy bạn cũng có thể sử dụng phiên bản này:

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)thực hiện giống như ví dụ cuối cùng của bạn trong một dòng.
glglgl

2
Tôi hiểu rằng mọi người muốn giải thích nếu khác sytnax, v.v., nhưng hợp nhất có một danh sách đối số tùy ý để điều này thực sự nên là câu trả lời hàng đầu.
Eric Twilegar

2
glglgl có câu trả lời tốt nhất. Tôi đã sử dụng timeit trên một mảng thử nghiệm lớn và việc triển khai giảm chậm không thể chấp nhận được, phiên bản đa dòng cho / if là nhanh nhất và việc thực hiện tiếp theo hơi chậm. Phiên bản tiếp theo là tổng thể tốt nhất khi xem xét sự đơn giản và ngắn gọn.
đất sét

3
@glglgl có đoạn trích thú vị. Thật không may vì Python không có pass-by-name, nên việc kết hợp như thế này không phải là ngắn mạch; tất cả các đối số được đánh giá trước khi mã chạy.
user1338062

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

5

Tôi nhận ra điều này đã được trả lời, nhưng có một lựa chọn khác khi bạn làm việc với các đối tượng.

Nếu bạn có một đối tượng có thể là:

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

Bạn có thể dùng:

obj.get(property_name, value_if_null)

Giống:

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

Bằng cách thêm {}làm giá trị mặc định, nếu "tên" bị thiếu, một đối tượng trống được trả về và chuyển qua get tiếp theo. Điều này tương tự như null-safe-navigation trong C #, sẽ giống như vậy obj?.name?.first.


Không phải tất cả các đối tượng đều có .get, điều này chỉ hoạt động đối với các đối tượng giống như chính tả
timdiels

Tôi đang gửi một chỉnh sửa câu trả lời để bao gồm getattr()cả.
dgw

gettrên dict không sử dụng tham số mặc định nếu giá trị là Không nhưng sử dụng tham số mặc định nếu giá trị không tồn tại vì khóa không nằm trong dict. Kết quả {'a': None}.get('a', 'I do not want None')vẫn sẽ cung cấp cho bạn None.
Patrick Mevzek

3

Ngoài câu trả lời của Juliano về hành vi của "hoặc": đó là "nhanh"

>>> 1 or 5/0
1

Vì vậy, đôi khi nó có thể là một lối tắt hữu ích cho những thứ như

object = getCachedVersion() or getFromDB()

17
Thuật ngữ bạn đang tìm kiếm là "ngắn mạch".
jpmc26

1

Ngoài ra, câu trả lời @Bothwells (mà tôi thích) cho các giá trị đơn lẻ, để kiểm tra xác nhận null của các giá trị trả về hàm, bạn có thể sử dụng toán tử walrus mới (kể từ python3.8):

def test():
    return

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

Do đó, testhàm không cần phải được đánh giá hai lần (như trong a = 2 if test() is None else test())


1

Trong trường hợp bạn cần lồng nhiều hơn một thao tác kết hợp null, chẳng hạn như:

model?.data()?.first()

Đây không phải là một vấn đề dễ dàng giải quyết với or. Nó cũng không thể được giải quyết .get()mà yêu cầu một loại từ điển hoặc tương tự (và dù sao cũng không thể được lồng nhau) hoặcgetattr() sẽ đưa ra một ngoại lệ khi noneType không có thuộc tính.

Pip liên quan xem xét thêm null kết hợp với ngôn ngữ là PEP 505 và cuộc thảo luận liên quan đến tài liệu nằm trong chuỗi ý tưởng python .


0

Về câu trả lời của @Hugh Bothwell, @mortehu và @glglgl.

Cài đặt bộ dữ liệu để thử nghiệm

import random

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

Xác định việc thực hiện

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)

Thực hiện chức năng kiểm tra

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

Kết quả trên mac i7 @ 2.7Ghz bằng 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

Rõ ràng not_none chức năng trả lời chính xác câu hỏi của OP và xử lý vấn đề "giả mạo". Nó cũng là nhanh nhất và dễ đọc nhất. Nếu áp dụng logic ở nhiều nơi, rõ ràng đó là cách tốt nhất để đi.

Nếu bạn gặp vấn đề trong đó bạn muốn tìm giá trị không null đầu tiên trong một lần lặp, thì phản hồi của @ mortehu là cách tốt nhất. Nhưng nó là một giải pháp cho một vấn đề khác với OP, mặc dù nó có thể xử lý một phần trường hợp đó. Nó không thể lấy một giá trị lặp và VÀ mặc định. Đối số cuối cùng sẽ là giá trị mặc định được trả về, nhưng sau đó bạn sẽ không chuyển qua một lần lặp trong trường hợp đó cũng như không rõ ràng rằng đối số cuối cùng là giá trị mặc định.

Sau đó, bạn có thể làm bên dưới, nhưng tôi vẫn sử dụng not_nullcho trường hợp sử dụng giá trị đơn.

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

0

Đối với những người như tôi đã vấp ngã ở đây để tìm kiếm một giải pháp khả thi cho vấn đề này, khi biến số có thể không được xác định, thì gần nhất tôi nhận được là:

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

Lưu ý rằng một chuỗi là cần thiết khi kiểm tra trong toàn cầu, nhưng sau đó biến thực tế được sử dụng khi kiểm tra giá trị.

Thông tin thêm về sự tồn tại của biến: Làm cách nào để kiểm tra xem một biến có tồn tại không?


1
(variablename or False) == Truegiống nhưvariablename == 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

nếu bạn không thể tìm thấy tên trong từ điển, nó sẽ trả về default_value, nếu tên tồn tại thì nó sẽ thêm bất kỳ giá trị hiện có nào bằng 1.

hy vọng điều này có thể giúp


1
Xin chào, chào mừng bạn đến với Stack Overflow. Những thông tin mới nào mà câu trả lời của bạn thêm vào mà chưa được bao phủ bởi các câu trả lời hiện có? Xem câu trả lời của @ Craig chẳng hạn
Gricey

-6

Hai hàm dưới đây tôi thấy rất hữu ích khi xử lý nhiều trường hợp kiểm thử biến.

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
Loại điều này thêm vào một loạt các thuật ngữ hơi khác nhau (ví dụ: "null" và "nz" không có nghĩa gì trong ngữ cảnh của Python), được nhập từ các ngôn ngữ khác, cộng với các biến thể (nghiêm ngặt hoặc không nghiêm ngặt!). Điều này chỉ thêm sự nhầm lẫn. Kiểm tra rõ ràng "là Không" là những gì bạn nên sử dụng. Thêm vào đó, bạn không nhận được lợi ích của bất kỳ ngữ nghĩa ngắn gọn nào mà các nhà khai thác có thể làm khi bạn sử dụng lệnh gọi hàm.
spookylukey 7/2/2017
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.