Я люблю лайнеры Python One:

u = payload.get("actor", {}).get("username", "")

Проблема, с которой я сталкиваюсь, заключается в том, что я не контролирую, что содержит «полезная нагрузка», кроме того, что знаю, что это словарь. Таким образом, если в «полезной нагрузке» нет «субъекта» или он есть, а у «актера» есть или нет «имя пользователя», этот однострочник подходит.

Проблема, конечно, возникает, когда у полезного груза есть актер, но актер не является словарем.

Есть ли такой прекрасный способ сделать это всесторонне, как один вкладыш, и рассмотреть возможность того, что «актер» может не быть словарем?

Конечно, я могу проверить тип, используя 'isinstance', но это не так хорошо.

Я не требую одного вкладыша как такового, просто требую наиболее эффективного способа обеспечения того, чтобы 'u' заполнялось без исключения и без предварительного знания того, что именно находится в 'полезной нагрузке'.

5
hikaru 18 Дек 2015 в 17:08

4 ответа

Лучший ответ

Использование EAFP

Как предполагает xnx, вы можете воспользоваться следующей парадигмой Python:

Проще просить прощения, чем разрешения

Вы также можете использовать его на KeyError:

try:
    u = payload["actor"]["username"]
except (AttributeError, KeyError):
    u = ""

Использование обертки с прощающей индексацией

Иногда было бы неплохо иметь что-то вроде нулевых условных операторов в Python. С помощью некоторого вспомогательного класса это можно сжать в однострочное выражение:

class Forgive:
  def __init__(self, value = None):
    self.value = value
  def __getitem__(self, name):
    if self.value is None:
      return Forgive()
    try:
      return Forgive(self.value.__getitem__(name))
    except (KeyError, AttributeError):
      return Forgive()
  def get(self, default = None):
    return default if self.value is None else self.value

data = {'actor':{'username': 'Joe'}}
print(Forgive(data)['actor']['username'].get('default1'))
print(Forgive(data)['actor']['address'].get('default2'))

Ps: кроме __getitem__ можно также переопределить __getattr__, так что вы даже можете написать Forgive(data)['actor'].username.get('default1').

9
Tamas Hegedus 18 Дек 2015 в 15:27

Hege_hegedus дал правильный ответ, однако есть одно предостережение в том, что обработка исключений намного медленнее, чем выполнение оператора if..else.

Например, если вы перебираете более тысячи payload объектов, и запись об актере лишь иногда не является словарем, этот код является абсолютно допустимым.

Однако, если вы перебираете более тысячи payload объектов, а каждая другая запись actor не является словарем, вам лучше использовать этот код.

u = ''
if 'actor' in payload and isinstance(payload['actor'], dict):
    u = payload['actor'].get('username', '')

Для получения дополнительной информации перейдите сюда - https://mail.python.org/ pipermail / Репетитор / 2011 - январь / 081143.html

ОБНОВИТЬ

Также оператор кода может быть переписан как однострочный, хотя и не так легко читаемый, как двухстрочный

u = payload['actor'].get('username', '') if 'actor' in payload and isinstance(payload['actor'], dict) else ''
0
Alex Volkov 18 Дек 2015 в 15:27

Почему бы не использовать исключение:

try:
    u = payload.get("actor", {}).get("username", "")
except AttributeError:
    u = ""
4
xnx 18 Дек 2015 в 14:13

Если вам действительно нужно сделать это в 1 строку, вам придется реализовать функциональность самостоятельно. Что стоит делать, если вы много раз используете эту семантику в своей программе.

Есть два способа сделать это: функция или пользовательский словарь-подобный объект для payload.

1) Функция обрабатывает случай, когда actor не является dict. Он может проверить isinstance или выполнить try или что-то еще - это не обязательно. Использование будет выглядеть примерно так: u = get("username", "", payload.get("actor", {})) или u = get("", payload, 'actor', 'username') (с произвольным количеством вложенных вызовов элементов в payload).

2) Класс пользовательских объектов - мощная вещь - делайте это, если вам действительно нужна эта абстракция в программе. Потомок dict или UserDict (в Python3) может проверить, что он хранит или выводит при вызовах __getitem__.

-1
xealits 12 Июл 2016 в 18:27