Skip to main content

weakget - Chain multiple `getattr` and `.get` calls into one expression

Project description

weakget - Chaining getattr and .get

With weakget, you can write code like:

x = weakget(obj)[5]['key'].attr.method() % 'default'

and x will be set to 'default' if:

  • obj has just 3 items, or
  • obj[5] is missing 'key', or
  • obj[5]['key'] doesn't have attr, or
  • obj[5]['key'].attr didn't define method()

Otherwise, x gets set to obj[5]['key'].attr.method(). Similar code in pure Python would look like:

try:
  x = obj[5]['key'].attr.method()
except (LookupError, AttributeError):
  x = 'default'

weakget is better because:

  • it doesn't hide AttributeError raised from obj[5] or obj[5]['key']
  • it doesn't hide LookupError raised from obj[5]['key'].attr or obj[5]['key'].attr.method
  • it doesn't hide any exception raised from calling obj[5]['key'].attr.method()
  • it fits on one line!

Usage

Install from PyPI using pip:

pip install weakget

Then import into your scripts:

>>> from weakget import weakget
>>> obj = []
>>> weakget(obj)[5]['key'].attr.method() % 'default'
'default'

pep505 - None-aware operations à la PEP 505

But wait, there's more! PEP 505 describes adding None-aware operators to Python, but you can get that behaviour today from pep505:

from weakget import pep505
x = pep505(a.attr, b.attr) % 3    # i.e. a.attr ?? b.attr ?? 3
y = pep505(c)['key'].attr % None  # i.e. c?.['key']?.attr

Behind-the-scenes, pep505 works in much the same way as weakget, but where weakget looks for LookupError or AttributeError to be raised, pep505 only looks for None. Similar code in pure Python would look like:

x = a.attr if a.attr is not None else b.attr if b.attr is not None else 3
y = c['key'].attr if (c is not None and c['key'] is not None) else None

pep505 is better because:

  • a.attr, b.attr, and c['key'] are each evaluated at most once
  • less typing, which also means...
  • less chance for logic errors

FAQs

Q: Why not use getattr's default instead of catching, and possibly hiding, AttributeErrors?

A: Turns out getattr also catches AttributeError:

>>> class A:
...   @property
...   def b(self):
...     return None.badattr
>>> getattr(A(), 'b')
AttributeError: 'NoneType' object has no attribute 'badattr'
>>> getattr(A(), 'b', 'default')
'default'

Q: Why not use .get's default argument instead of catching LookupError?

A: .get is implemented on mapping objects like dict, but is not available on sequence objects like list. Catching the LookupError from obj[key] is the only way to support both.

Q: Why % instead of or, |, or a method?

A: or isn't an option as it cannot be directly overloaded. | is enticing as a | b reads like "a or b", but "or" in Python usually refers to truthiness, which does not distinguish "lack of value" from "false". Adding a method would hide any method of the same name in the underlying object. % is already overloaded in Python, making it the best option among the remaining operators.

Q: Can't you come up with a better name than pep505?

A: Well, PP is currently in the lead as it looks like ??, but I'm open to suggestions!

Project details


Release history Release notifications | RSS feed

This version

1.0

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

weakget-1.0.tar.gz (8.0 kB view hashes)

Uploaded Source

Built Distribution

weakget-1.0-py2.py3-none-any.whl (4.9 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page