Skip to content

Instantly share code, notes, and snippets.

@davidwtbuxton
Created April 1, 2020 08:31
Show Gist options
  • Save davidwtbuxton/59ed802bfd24844a41d236a62666a9b1 to your computer and use it in GitHub Desktop.
Save davidwtbuxton/59ed802bfd24844a41d236a62666a9b1 to your computer and use it in GitHub Desktop.
Any vs Or in Python3
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "Any vs Or in Python3",
"provenance": [],
"authorship_tag": "ABX9TyPPV6kYcX18ppxmaj6jBC+q",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/davidwtbuxton/59ed802bfd24844a41d236a62666a9b1/any-vs-or-in-python3.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "F69Y0-URuC5w",
"colab_type": "text"
},
"source": [
"https://realpython.com/any-python/\n",
"\n",
"\n",
"> By default, or evaluates conditions lazily, whereas any does not.\n",
"\n",
"This is wrong. Any will return on encountering the first True value, without evaluating the remaining arguments.\n",
"\n",
"I think the confusion is a result of Any requiring an iterable, but there's no syntax for writing a _literal_ lazy iterable, only lists or tuples."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Wa7vheIYuteo",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 85
},
"outputId": "1f31ee6c-ba05-451a-854e-27ab3754aef3"
},
"source": [
"def side_effect_true():\n",
" global call_count\n",
" call_count += 1\n",
" return True\n",
"\n",
"# With \"or\" the 2nd argument is not evaluated, so the global call count is 1.\n",
"call_count = 0\n",
"print(side_effect_true() or side_effect_true())\n",
"print(\"Called\", call_count)\n",
"\n",
"# With \"any\" and a list, both list elements are evaluated, and the count is 2.\n",
"call_count = 0\n",
"print(any([side_effect_true(), side_effect_true()]))\n",
"print(\"Called\", call_count)"
],
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"text": [
"True\n",
"Called 1\n",
"True\n",
"Called 2\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bzbpoWiawrdJ",
"colab_type": "text"
},
"source": [
"The effect of using a list (or tuple) to Any is that all elements of the list are evaluated.\n",
"\n",
"But a generator is a valid iterable. Changing the argument to Any from a list to a generator shows that Any returns as soon as it gets a true value in the iterable."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Ider29R1w7b-",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 51
},
"outputId": "7d2200a0-0f55-4e0f-fb08-ec2de674213f"
},
"source": [
"def lazy_iterable():\n",
" yield side_effect_true()\n",
" yield side_effect_true()\n",
"\n",
"# With a generator, \"any\" stops at the first true value, the count is 1.\n",
"call_count = 0\n",
"print(any(lazy_iterable()))\n",
"print(\"Called\", call_count)"
],
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"text": [
"True\n",
"Called 1\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "rLQ3_ymzy87H",
"colab_type": "text"
},
"source": [
"I think creating a generator that wraps a few callables, in order to avoid unnecessary evaluation is probably more complicated than using \"or\". It would depend on the number of items, and the penalty of evaluating each item.\n",
"\n",
"Another approach would be to use generator expressions, but I don't see a nice way of having a small set of items in the iterable without evaluating every item, which defeats the point of using the generator expression with Any.\n",
"\n",
"But there is a case where you want to evaluate the _same_ item many times.\n",
"\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "squNTlMI0cwZ",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 51
},
"outputId": "edcfa591-17f2-4cef-c2cc-9c54274b2639"
},
"source": [
"# This is a generator expression. Items are evaluated when the expression\n",
"# is used for iteration (like in a for loop or with next).\n",
"lazy_iterable = (side_effect_true() for _ in range(100))\n",
"\n",
"call_count = 0\n",
"print(any(lazy_iterable))\n",
"print(\"Called\", call_count)\n"
],
"execution_count": 12,
"outputs": [
{
"output_type": "stream",
"text": [
"True\n",
"Called 1\n"
],
"name": "stdout"
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment