Подтвердить что ты не робот

Может быть, "вид" монады в Python

Попытка найти способ очистки некоторых моих кодов.

Итак, у меня есть что-то подобное в моем коде Python:

company = None
country = None

person = Person.find(id=12345)
if person is not None: # found        
    company = Company.find(person.companyId)

    if company is not None:
         country = Country.find(company.countryId)

return (person, company, country)

Прочитав учебник по монахам Haskell (в частности, возможно), мне было интересно, можно ли записать его по-другому.

4b9b3361

Ответ 1

company = country = None
try:
    person  =  Person.find(id=12345)
    company = Company.find(person.companyId)
    country = Country.find(company.countryId)
except AttributeError:
    pass # `person` or `company` might be None

EAFP

Ответ 2

Используйте поведение короткого замыкания и чтобы по умолчанию был настроен пользовательский объект, а None - false:

person  = Person.find(id=12345)
company = person and person.company
country = company and company.country

Ответ 3

Python не имеет особо приятного синтаксиса для монад. Если вы хотите ограничить себя использованием чего-то вроде монады Maybe (подразумевая, что вы сможете использовать Maybe, вы не сможете создавать общие функции, которые имеют дело с любой монадой), вы можете использовать следующий подход:

class Maybe():
    def andThen(self, action): # equivalent to Haskell >>=
        if self.__class__ == _Maybe__Nothing:
            return Nothing
        elif self.__class__ == Just:
            return action(self.value)

    def followedBy(self, action): # equivalent to Haskell >>
        return self.andThen(lambda _: action)

class _Maybe__Nothing(Maybe):
    def __repr__(self):
        return "Nothing"

Nothing = _Maybe__Nothing()

class Just(Maybe):
    def __init__(self, v):
        self.value = v
    def __repr__(self):
        return "Just(%r)" % self.value

Затем сделайте все методы, которые в настоящее время возвращают None, вместо этого возвращают Just(value) или Nothing. Это позволяет вам написать этот код:

Person.find(id=12345).andThen(lambda person: Company.find(person.companyId)).andThen(lambda company: Country.find(company.countryId))

Вы можете, конечно, адаптировать лямбда для хранения промежуточных результатов в переменных; это вам, как это сделать правильно.

Ответ 4

Вы проверяли PyMonad?

https://pypi.python.org/pypi/PyMonad/

Он включает в себя не только монаду Maybe, но и монаду списка, классы Functor и Applicative. Моноиды и многое другое.

В вашем случае это будет что-то вроде:

country = Person.find(id=12345)          >> (lambda company: 
          Company.find(person.companyId) >> (lambda country: 
          Country.find(company.countryId))

Легче понять и чище, чем EAFP.

Ответ 5

person = Person.find(id=12345)
company = None if person is None else Company.find(person.companyId)
country = None if company is None else Country.find(company.countryId)

return (person, company, country)

Ответ 6

Я думаю, что это идеально подходит для:

getattr (объект, имя [, по умолчанию])

Я нахожу getattr существенным при работе с объектами и атрибутами. Это в значительной степени эквивалентно dict.get(key[, default]).

person  = Person.find(id=12345)
company = person and getattr(person, 'company', None)
country = company and getattr(company, 'country', None)

Ответ 7

Больше "Pythonic", чем пытаться реализовать другую парадигму (не то, что это не интересно и круто), было бы добавить интеллект к вашим объектам, чтобы они могли найти свои атрибуты (и независимо от того, существуют они вообще) сами по себе.

Ниже приведен пример базового класса, который использует ваш метод "find" и корреляцию имен атрибутов идентификатора и имен классов для работы с вашим примером. Я ввел минимальные классы Person и Company для поиска компании работа:

class Base(object):
    def __getattr__(self, attr):
        if hasattr(self, attr + "Id"):
            return globals()[attr.title()].find(getattr(self, attr + "Id"))
        return None
    @classmethod
    def find(cls, id):
        return "id %d " % id

class Person(Base):
    companyId=5

class Company(Base):
    pass

И на консоли, после вставки кода выше:

>>> p = Person()
>>> p.company
'id 5 '

С помощью этого Base ваш код выше может быть просто:

person = Person.find(id=12345)
company = person.company
country = company and company.country