One place for hosting & domains

      Function

      How To Use unittest to Write a Test Case for a Function in Python


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      The Python standard library includes the unittest module to help you write and run tests for your Python code.

      Tests written using the unittest module can help you find bugs in your programs, and prevent regressions from occurring as you change your code over time. Teams adhering to test-driven development may find unittest useful to ensure all authored code has a corresponding set of tests.

      In this tutorial, you will use Python’s unittest module to write a test for a function.

      Prerequisites

      To get the most out of this tutorial, you’ll need:

      Defining a TestCase Subclass

      One of the most important classes provided by the unittest module is named TestCase. TestCase provides the general scaffolding for testing our functions. Let’s consider an example:

      test_add_fish_to_aquarium.py

      import unittest
      
      def add_fish_to_aquarium(fish_list):
          if len(fish_list) > 10:
              raise ValueError("A maximum of 10 fish can be added to the aquarium")
          return {"tank_a": fish_list}
      
      
      class TestAddFishToAquarium(unittest.TestCase):
          def test_add_fish_to_aquarium_success(self):
              actual = add_fish_to_aquarium(fish_list=["shark", "tuna"])
              expected = {"tank_a": ["shark", "tuna"]}
              self.assertEqual(actual, expected)
      

      First we import unittest to make the module available to our code. We then define the function we want to test—here it is add_fish_to_aquarium.

      In this case our add_fish_to_aquarium function accepts a list of fish named fish_list, and raises an error if fish_list has more than 10 elements. The function then returns a dictionary mapping the name of a fish tank "tank_a" to the given fish_list.

      A class named TestAddFishToAquarium is defined as a subclass of unittest.TestCase. A method named test_add_fish_to_aquarium_success is defined on TestAddFishToAquarium. test_add_fish_to_aquarium_success calls the add_fish_to_aquarium function with a specific input and verifies that the actual returned value matches the value we’d expect to be returned.

      Now that we’ve defined a TestCase subclass with a test, let’s review how we can execute that test.

      Executing a TestCase

      In the previous section, we created a TestCase subclass named TestAddFishToAquarium. From the same directory as the test_add_fish_to_aquarium.py file, let’s run that test with the following command:

      • python -m unittest test_add_fish_to_aquarium.py

      We invoked the Python library module named unittest with python -m unittest. Then, we provided the path to our file containing our TestAddFishToAquarium TestCase as an argument.

      After we run this command, we receive output like the following:

      Output

      . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK

      The unittest module ran our test and told us that our test ran OK. The single . on the first line of the output represents our passed test.

      Note: TestCase recognizes test methods as any method that begins with test. For example, def test_add_fish_to_aquarium_success(self) is recognized as a test and will be run as such. def example_test(self), conversely, would not be recognized as a test because it does not begin with test. Only methods beginning with test will be run and reported when you run python -m unittest ....

      Now let’s try a test with a failure.

      We modify the following highlighted line in our test method to introduce a failure:

      test_add_fish_to_aquarium.py

      import unittest
      
      def add_fish_to_aquarium(fish_list):
          if len(fish_list) > 10:
              raise ValueError("A maximum of 10 fish can be added to the aquarium")
          return {"tank_a": fish_list}
      
      
      class TestAddFishToAquarium(unittest.TestCase):
          def test_add_fish_to_aquarium_success(self):
              actual = add_fish_to_aquarium(fish_list=["shark", "tuna"])
              expected = {"tank_a": ["rabbit"]}
              self.assertEqual(actual, expected)
      

      The modified test will fail because add_fish_to_aquarium won’t return "rabbit" in its list of fish belonging to "tank_a". Let’s run the test.

      Again, from the same directory as test_add_fish_to_aquarium.py we run:

      • python -m unittest test_add_fish_to_aquarium.py

      When we run this command, we receive output like the following:

      Output

      F ====================================================================== FAIL: test_add_fish_to_aquarium_success (test_add_fish_to_aquarium.TestAddFishToAquarium) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_add_fish_to_aquarium.py", line 13, in test_add_fish_to_aquarium_success self.assertEqual(actual, expected) AssertionError: {'tank_a': ['shark', 'tuna']} != {'tank_a': ['rabbit']} - {'tank_a': ['shark', 'tuna']} + {'tank_a': ['rabbit']} ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (failures=1)

      The failure output indicates that our test failed. The actual output of {'tank_a': ['shark', 'tuna']} did not match the (incorrect) expectation we added to test_add_fish_to_aquarium.py of: {'tank_a': ['rabbit']}. Notice also that instead of a ., the first line of the output now has an F. Whereas . characters are outputted when tests pass, F is the output when unittest runs a test that fails.

      Now that we’ve written and run a test, let’s try writing another test for a different behavior of the add_fish_to_aquarium function.

      Testing a Function that Raises an Exception

      unittest can also help us verify that the add_fish_to_aquarium function raises a ValueError Exception if given too many fish as input. Let’s expand on our earlier example, and add a new test method named test_add_fish_to_aquarium_exception:

      test_add_fish_to_aquarium.py

      import unittest
      
      def add_fish_to_aquarium(fish_list):
          if len(fish_list) > 10:
              raise ValueError("A maximum of 10 fish can be added to the aquarium")
          return {"tank_a": fish_list}
      
      
      class TestAddFishToAquarium(unittest.TestCase):
          def test_add_fish_to_aquarium_success(self):
              actual = add_fish_to_aquarium(fish_list=["shark", "tuna"])
              expected = {"tank_a": ["shark", "tuna"]}
              self.assertEqual(actual, expected)
      
          def test_add_fish_to_aquarium_exception(self):
              too_many_fish = ["shark"] * 25
              with self.assertRaises(ValueError) as exception_context:
                  add_fish_to_aquarium(fish_list=too_many_fish)
              self.assertEqual(
                  str(exception_context.exception),
                  "A maximum of 10 fish can be added to the aquarium"
              )
      

      The new test method test_add_fish_to_aquarium_exception also invokes the add_fish_to_aquarium function, but it does so with a 25 element long list containing the string "shark" repeated 25 times.

      test_add_fish_to_aquarium_exception uses the with self.assertRaises(...) context manager provided by TestCase to check that add_fish_to_aquarium rejects the inputted list as too long. The first argument to self.assertRaises is the Exception class that we expect to be raised—in this case, ValueError. The self.assertRaises context manager is bound to a variable named exception_context. The exception attribute on exception_context contains the underlying ValueError that add_fish_to_aquarium raised. When we call str() on that ValueError to retrieve its message, it returns the correct exception message we expected.

      From the same directory as test_add_fish_to_aquarium.py, let’s run our test:

      • python -m unittest test_add_fish_to_aquarium.py

      When we run this command, we receive output like the following:

      Output

      .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK

      Notably, our test would have failed if add_fish_to_aquarium either didn’t raise an Exception, or raised a different Exception (for example TypeError instead of ValueError).

      Note: unittest.TestCase exposes a number of other methods beyond assertEqual and assertRaises that you can use. The full list of assertion methods can be found in the documentation, but a selection are included here:

      Method Assertion
      assertEqual(a, b) a == b
      assertNotEqual(a, b) a != b
      assertTrue(a) bool(a) is True
      assertFalse(a) bool(a) is False
      assertIsNone(a) a is None
      assertIsNotNone(a) a is not None
      assertIn(a, b) a in b
      assertNotIn(a, b) a not in b

      Now that we’ve written some basic tests, let’s see how we can use other tools provided by TestCase to harness whatever code we are testing.

      Using the setUp Method to Create Resources

      TestCase also supports a setUp method to help you create resources on a per-test basis. setUp methods can be helpful when you have a common set of preparation code that you want to run before each and every one of your tests. setUp lets you put all this preparation code in a single place, instead of repeating it over and over for each individual test.

      Let’s take a look at an example:

      test_fish_tank.py

      import unittest
      
      class FishTank:
          def __init__(self):
              self.has_water = False
      
          def fill_with_water(self):
              self.has_water = True
      
      class TestFishTank(unittest.TestCase):
          def setUp(self):
              self.fish_tank = FishTank()
      
          def test_fish_tank_empty_by_default(self):
              self.assertFalse(self.fish_tank.has_water)
      
          def test_fish_tank_can_be_filled(self):
              self.fish_tank.fill_with_water()
              self.assertTrue(self.fish_tank.has_water)
      

      test_fish_tank.py defines a class named FishTank. FishTank.has_water is initially set to False, but can be set to True by calling FishTank.fill_with_water(). The TestCase subclass TestFishTank defines a method named setUp that instantiates a new FishTank instance and assigns that instance to self.fish_tank.

      Since setUp is run before every individual test method, a new FishTank instance is instantiated for both test_fish_tank_empty_by_default and test_fish_tank_can_be_filled. test_fish_tank_empty_by_default verifies that has_water starts off as False. test_fish_tank_can_be_filled verifies that has_water is set to True after calling fill_with_water().

      From the same directory as test_fish_tank.py, we can run:

      • python -m unittest test_fish_tank.py

      If we run the previous command, we will receive the following output:

      Output

      .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK

      The final output shows that the two tests both pass.

      setUp allows us to write preparation code that is run for all of our tests in a TestCase subclass.

      Note: If you have multiple test files with TestCase subclasses that you’d like to run, consider using python -m unittest discover to run more than one test file. Run python -m unittest discover --help for more information.

      Using the tearDown Method to Clean Up Resources

      TestCase supports a counterpart to the setUp method named tearDown. tearDown is useful if, for example, we need to clean up connections to a database, or modifications made to a filesystem after each test completes. We’ll review an example that uses tearDown with filesystems:

      test_advanced_fish_tank.py

      import os
      import unittest
      
      class AdvancedFishTank:
          def __init__(self):
              self.fish_tank_file_name = "fish_tank.txt"
              default_contents = "shark, tuna"
              with open(self.fish_tank_file_name, "w") as f:
                  f.write(default_contents)
      
          def empty_tank(self):
              os.remove(self.fish_tank_file_name)
      
      
      class TestAdvancedFishTank(unittest.TestCase):
          def setUp(self):
              self.fish_tank = AdvancedFishTank()
      
          def tearDown(self):
              self.fish_tank.empty_tank()
      
          def test_fish_tank_writes_file(self):
              with open(self.fish_tank.fish_tank_file_name) as f:
                  contents = f.read()
              self.assertEqual(contents, "shark, tuna")
      

      test_advanced_fish_tank.py defines a class named AdvancedFishTank. AdvancedFishTank creates a file named fish_tank.txt and writes the string "shark, tuna" to it. AdvancedFishTank also exposes an empty_tank method that removes the fish_tank.txt file. The TestAdvancedFishTank TestCase subclass defines both a setUp and tearDown method.

      The setUp method creates an AdvancedFishTank instance and assigns it to self.fish_tank. The tearDown method calls the empty_tank method on self.fish_tank: this ensures that the fish_tank.txt file is removed after each test method runs. This way, each test starts with a clean slate. The test_fish_tank_writes_file method verifies that the default contents of "shark, tuna" are written to the fish_tank.txt file.

      From the same directory as test_advanced_fish_tank.py let’s run:

      • python -m unittest test_advanced_fish_tank.py

      We will receive the following output:

      Output

      . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK

      tearDown allows you to write cleanup code that is run for all of your tests in a TestCase subclass.

      Conclusion

      In this tutorial, you have written TestCase classes with different assertions, used the setUp and tearDown methods, and run your tests from the command line.

      The unittest module exposes additional classes and utilities that you did not cover in this tutorial. Now that you have a baseline, you can use the unittest module’s documentation to learn more about other available classes and utilities. You may also be interested in How To Add Unit Testing to Your Django Project.



      Source link

      How To Use the Python Map Function


      Introduction

      We can use the Python built-in function map() to apply a function to each item in an iterable (like a list or dictionary) and return a new iterator for retrieving the results. map() returns a map object (an iterator), which we can use in other parts of our program. We can also pass the map object to the list() function, or another sequence type, to create an iterable.

      The syntax for the map() function is as follows:

      map(function, iterable, [iterable 2, iterable 3, ...])
      

      Instead of using a for loop, the map() function provides a way of applying a function to every item in an iterable. Therefore it can often be more performant since it is only applying the function one item at a time rather than making copies of the items into another iterable. This is particularly useful when working on programs processing large data sets. map() can also take multiple iterables as arguments to the function by sending one item from each iterable to the function at a time.

      In this tutorial, we’ll review three different ways of working with map(): with a lambda function, with a user-defined function, and finally with a built-in function using multiple iterable arguments.

      Using a Lambda Function

      The first argument to map() is a function, which we use to apply to each item. Python calls the function once for every item in the iterable we pass into map() and it returns the manipulated item within a map object. For the first function argument, we can either pass a user-defined function or we can make use of lambda functions, particularly when the expression is less complex.

      The syntax of map() with a lambda function is as follows:

      map(lambda item: item[] expression, iterable)
      

      With a list like the following, we can implement a lambda function with an expression that we want to apply to each item in our list:

      numbers = [10, 15, 21, 33, 42, 55]
      

      To apply an expression against each of our numbers, we can use map() and lambda:

      mapped_numbers = list(map(lambda x: x * 2 + 3, numbers))
      

      Here we declare an item in our list as x. Then we add our expression. We pass in our list of numbers as the iterable for map().

      In order to receive the results of this immediately we print a list of the map object:

      print(mapped_numbers)
      

      Output

      [23, 33, 45, 69, 87, 113]

      We have used list() so that the map object is returned to us as a list, rather than a less human-readable object like: <map object at 0x7fc250003a58>. The map object is an iterator over our results, so we could loop over it with for or we can use list() to turn it into a list. We’re doing this here because it’s a good way to review the results.

      Ultimately map() is most useful when working with large datasets, so we would likely work with the map object further, and generally would not be using a constructor like list() on them.

      For smaller datasets, list comprehensions may be more suitable, but for the purposes of this tutorial we’re using a small dataset to demonstrate map().

      Implementing a User-defined Function

      Similarly to a lambda we can use a function we have defined to apply to an iterable. While lambda functions are more useful to implement when you’re working with a one-line expression, user-defined functions are more appropriate when the expression grows in complexity. Furthermore, when we need to pass another piece of data to the function that you’re applying to your iterable, user-defined functions can be a better choice for readability.

      For example, in the following iterable, each item is a dictionary that contains different details about each of our aquarium creatures:

      aquarium_creatures = [
          {"name": "sammy", "species": "shark", "tank number": 11, "type": "fish"},
          {"name": "ashley", "species": "crab", "tank number": 25, "type": "shellfish"},
          {"name": "jo", "species": "guppy", "tank number": 18, "type": "fish"},
          {"name": "jackie", "species": "lobster", "tank number": 21, "type": "shellfish"},
          {"name": "charlie", "species": "clownfish", "tank number": 12, "type": "fish"},
          {"name": "olly", "species": "green turtle", "tank number": 34, "type": "turtle"}
      ]
      

      We’ve decided that all the aquarium creatures are in fact going to move into the same tank. We need to update our records to reflect that all of our creatures are moving into tank 42. To have map() access each dictionary and each key:value pair in the dictionaries, we construct a nested function:

      def assign_to_tank(aquarium_creatures, new_tank_number):
          def apply(x):
              x["tank number"] = new_tank_number
              return x
          return map(apply, aquarium_creatures)
      

      We define an assign_to_tank() function that takes aquarium_creatures and new_tank_number as parameters. In assign_to_tank() we pass apply() as the function to map() on the final line. The assign_to_tank function will return the iterator resulting from map().

      apply() takes x as an argument, which represents an item in our list — a single dictionary.

      Next we define that x is the "tank number" key from aquarium_creatures and that it should store the passed in new_tank_number. We return each item after applying the new tank number.

      We call assign_to_tank() with our list of dictionaries and the new tank number we want to replace for each creature:

      assigned_tanks = assign_to_tank(aquarium_creatures, 42)
      

      Once the function completes we have our map object stored in the assigned_tanks variable, which we turn into a list and print:

      print(list(assigned_tanks))
      

      We’ll receive the following output from this program:

      Output

      [{'name': 'sammy', 'species': 'shark', 'tank number': 42, 'type': 'fish'}, {'name': 'ashley', 'species': 'crab', 'tank number': 42, 'type': 'shellfish'}, {'name': 'jo', 'species': 'guppy', 'tank number': 42, 'type': 'fish'}, {'name': 'jackie', 'species': 'lobster', 'tank number': 42, 'type': 'shellfish'}, {'name': 'charlie', 'species': 'clownfish', 'tank number': 42, 'type': 'fish'}, {'name': 'olly', 'species': 'green turtle', 'tank number': 42, 'type': 'turtle'}]

      We’ve mapped the new tank number to our list of dictionaries. Using a function that we define, we can incorporate map() to apply the function efficiently on each item of the list.

      Using a Built-in Function with Multiple Iterables

      In the same way as lambda functions or our own defined functions, we can use Python built-in functions with map(). To apply a function with multiple iterables, we pass in another iterable name following the first one. For example, using the pow() function that takes in two numbers to find the power of the base number to the provided exponent.

      Here we have our lists of integers that we would like to use with pow():

      base_numbers = [2, 4, 6, 8, 10]
      powers = [1, 2, 3, 4, 5]
      

      Next we pass in pow() as our function into map() and provide the two lists as our iterables:

      numbers_powers = list(map(pow, base_numbers, powers))
      
      print(numbers_powers)
      

      map() will apply the pow() function to the same item in each list to provide the power. Therefore our results will show 2**1, 4**2, 6**3, and so on:

      Output

      [2, 16, 216, 4096, 100000]

      If we were to provide map() with an iterable that was longer than the other, map() would stop calculating once it reaches the end of the shortest iterable. In the following program we’re extending base_numbers with three additional numbers:

      base_numbers = [2, 4, 6, 8, 10, 12, 14, 16]
      powers = [1, 2, 3, 4, 5]
      
      numbers_powers = list(map(pow, base_numbers, powers))
      
      print(numbers_powers)
      

      As a result, nothing will change within the calculation of this program and so it will still yield the same result:

      Output

      [2, 16, 216, 4096, 100000]

      We’ve used the map() function with a Python built-in function and have seen that it can handle multiple iterables. We’ve also reviewed that map() will continue to process multiple iterables until it has reached the end of the iterable with the fewest items.

      Conclusion

      In this tutorial, we’ve learned the different ways of using the map() function in Python. Now you can use map() with your own function, a lambda function, and with any other built-in functions. You can also implement map() with functions that require multiple iterables.

      In this tutorial, we printed the results from map() immediately to a list format for demonstration purposes. In our programs we would typically use the returned map object to further manipulate the data.

      If you would like to learn more Python, check out our How To Code in Python 3 series and our Python topic page. To learn more about working with data sets in functional programming, check out our article on the filter() function.



      Source link

      How To Use the Python Filter Function


      Introduction

      The Python built-in filter() function can be used to create a new iterator from an existing iterable (like a list or dictionary) that will efficiently filter out elements using a function that we provide. An iterable is a Python object that can be “iterated over”, that is, it will return items in a sequence such that we can use it in a for loop.

      The basic syntax for the filter() function is:

      filter(function, iterable)
      

      This will return a filter object, which is an iterable. We can use a function like list() to make a list of all the items returned in a filter object.

      The filter() function provides a way of filtering values that can often be more efficient than a list comprehension, especially when we’re starting to work with larger data sets. For example, a list comprehension will make a new list, which will increase the run time for that processing. This means that after our list comprehension has completed its expression, we’ll have two lists in memory. However, filter() will make a simple object that holds a reference to the original list, the provided function, and an index of where to go in the original list, which will take up less memory.

      In this tutorial, we’ll review four different ways of using filter(): with two different iterable structures, with a lambda function, and with no defined function.

      Using filter() with a Function

      The first argument to filter() is a function, which we use to decide whether to include or filter out each item. The function is called once for every item in the iterable passed as the second argument and each time it returns False, the value is dropped. As this argument is a function, we can either pass a normal function or we can make use of lambda functions, particularly when the expression is less complex.

      Following is the syntax of a lambda with filter():

      filter(lambda item: item[] expression, iterable)
      

      With a list, like the following, we can incorporate a lambda function with an expression against which we want to evaluate each item from the list:

      creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']
      

      To filter this list to find the names of our aquarium creatures that start with a vowel, we can run the following lambda function:

      print(list(filter(lambda x: x[0].lower() in 'aeiou', creature_names)))
      

      Here we declare an item in our list as x. Then we set our expression to access the first character of each string (or character “zero”), so x[0]. Lowering the case of each of the names ensures this will match letters to the string in our expression, 'aeiou'.

      Finally we pass the iterable creature_names. Like in the previous section we apply list() to the result in order to create a list from the iterator filter() returns.

      The output will be the following:

      Output

      ['Ashley', 'Olly']

      This same result can be achieved using a function we define:

      creature_names = ['Sammy', 'Ashley', 'Jo', 'Olly', 'Jackie', 'Charlie']
      
      def names_vowels(x):
        return x[0].lower() in 'aeiou'
      
      filtered_names = filter(names_vowels, creature_names)
      
      print(list(filtered_names))
      

      Our function names_vowels defines the expression that we will implement to filter creature_names.

      Again, the output would be as follows:

      Output

      ['Ashley', 'Olly']

      Overall, lambda functions achieve the same result with filter() as when we use a regular function. The necessity to define a regular function grows as the complexity of expressions for filtering our data increases, which is likely to promote better readability in our code.

      Using None with filter()

      We can pass None as the first argument to filter() to have the returned iterator filter out any value that Python considers “falsy”. Generally, Python considers anything with a length of 0 (such as an empty list or empty string) or numerically equivalent to 0 as false, thus the use of the term “falsy.”

      In the following case we want to filter our list to only show the tank numbers at our aquarium:

      aquarium_tanks = [11, False, 18, 21, "", 12, 34, 0, [], {}]
      

      In this code we have a list containing integers, empty sequences, and a boolean value.

      filtered_tanks = filter(None, aquarium_tanks)
      

      We use the filter() function with None and pass in the aquarium_tanks list as our iterable. Since we have passed None as the first argument, we will check if the items in our list are considered false.

      print(list(filtered_tanks))
      

      Then we wrap filtered_tanks in a list() function so that it returns a list for filtered_tanks when we print.

      Here we see the output shows only the integers. All the items that evaluated to False, that are equivalent to 0 in length, were removed by filter():

      Output

      [11, 25, 18, 21, 12, 34]

      Note: If we don’t use list() and print filtered_tanks we would receive a filter object something like this: <filter object at 0x7fafd5903240>. The filter object is an iterable, so we could loop over it with for or we can use list() to turn it into a list, which we’re doing here because it’s a good way to review the results.

      With None we have used filter() to quickly remove items from our list that were considered false.

      Using filter() with a List of Dictionaries

      When we have a more complex data structure, we can still use filter() to evaluate each of the items. For example, if we have a list of dictionaries, not only do we want to iterate over each item in the list — one of the dictionaries — but we may also want to iterate over each key:value pair in a dictionary in order to evaluate all the data.

      As an example, let’s say we have a list of each creature in our aquarium along with different details about each of them:

      aquarium_creatures = [
        {"name": "sammy", "species": "shark", "tank number": "11", "type": "fish"},
        {"name": "ashley", "species": "crab", "tank number": "25", "type": "shellfish"},
        {"name": "jo", "species": "guppy", "tank number": "18", "type": "fish"},
        {"name": "jackie", "species": "lobster", "tank number": "21", "type": "shellfish"},
        {"name": "charlie", "species": "clownfish", "tank number": "12", "type": "fish"},
        {"name": "olly", "species": "green turtle", "tank number": "34", "type": "turtle"}
      ]
      

      We want to filter this data by a search string we give to the function. To have filter() access each dictionary and each item in the dictionaries, we construct a nested function, like the following:

      def filter_set(aquarium_creatures, search_string):
          def iterator_func(x):
              for v in x.values():
                  if search_string in v:
                      return True
              return False
          return filter(iterator_func, aquarium_creatures)
      

      We define a filter_set() function that takes aquarium_creatures and search_string as parameters. In filter_set() we pass our iterator_func() as the function to filter(). The filter_set() function will return the iterator resulting from filter().

      The iterator_func() takes x as an argument, which represents an item in our list (that is, a single dictionary).

      Next the for loop accesses the values in each key:value pair in our dictionaries and then uses a conditional statement to check whether the search_string is in v, representing a value.

      Like in our previous examples, if the expression evaluates to True the function adds the item to the filter object. This will return once the filter_set() function has completed. We position return False outside of our loop so that it checks every item in each dictionary, instead of returning after checking the first dictionary alone.

      We call filter_set() with our list of dictionaries and the search string we want to find matches for:

      filtered_records = filter_set(aquarium_creatures, "2")    
      

      Once the function completes we have our filter object stored in the filtered_records variable, which we turn into a list and print:

      print(list(filtered_records))      
      

      We’ll see the following output from this program:

      Output

      [{'name': 'ashley', 'species': 'crab', 'tank number': '25', 'type': 'shellfish'}, {'name': 'jackie', 'species': 'lobster', 'tank number': '21', 'type': 'shellfish'}, {'name': 'charlie', 'species': 'clownfish', 'tank number': '12', 'type': 'fish'}]

      We’ve filtered the list of dictionaries with the search string 2. We can see that the three dictionaries that included a tank number with 2 have been returned. Using our own nested function allowed us to access every item and efficiently check each against the search string.

      Conclusion

      In this tutorial, we’ve learned the different ways of using the filter() function in Python. Now you can use filter() with your own function, a lambda function, or with None to filter for items in varying complexities of data structures.

      Although in this tutorial we printed the results from filter() immediately in list format, it is likely in our programs we would use the returned filter() object and further manipulate the data.

      If you would like to learn more Python, check out our How To Code in Python 3 series and our Python topic page.



      Source link