Cynic Cynic - 2 months ago 10
Python Question

A function that creates functions named after arguments passed in

Is there a way to make a function that makes other functions to be called later named after the variables passed in?

For the example let's pretend https://example.com/engine_list returns this xml file, when I call it in get_search_engine_xml

<engines>
<engine address="https://www.google.com/">Google</engine>
<engine address="https://www.bing.com/">Bing</engine>
<engine address="https://duckduckgo.com/">DuckDuckGo</engine>
</engines>


And here's my code:

import requests
import xml.etree.ElementTree as ET
base_url = 'https://example.com'

# This is what I'm trying to figure out how to do correctly, create a function
# named after the engine returned in get_search_engine_xml(), to be called later
def create_get_engine_function(function_name, address):
def function_name():
r = requests.get(address)
return function_name

def get_search_engine_xml():
url = base_url + '/engine_list'
r = requests.get(url)
engines_list = str(r.content)
engines_root = ET.fromstring(engines_list)
for child in engines_root:
engine_name = child.text.lower()
engine_address = child.attrib['address']
create_get_engine_function(engine_name, engine_address)

## Runs without error.
get_search_engine_xml()

## But if I try to call one of the functions.
google()


I get the following error.

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'google' is not defined


Defining engine_name and engine_address seems to be working when I log it out. So I'm pretty sure the problem lies in create_get_engine_function, which admittedly I don't know what I'm doing and I was trying to piece together from similar questions.

Can you name a function created by another function with an argument that's passed in? Is there a better way to do this?

Answer

You can assign them to globals()

def create_get_engine_function(function_name, address):
    def function():
        r = requests.get(address)

    function.__name__ = function_name
    function.__qualname__ = function_name  # for Python 3.3+ 
    globals()[function_name] = function

Although, depending on what you're actually trying to accomplish, a better design would be to store all the engine names/addresses in a dictionary and access them as needed:

# You should probably should rename this to 'parse_engines_from_xml'
def get_search_engine_xml(): 
    ...
    search_engines = {} # maps names to addresses
    for child in engines_root:
        ...
        search_engines[engine_name] = engine_address
    return search_engines

engines = get_search_engine_xml()

e = requests.get(engines['google'])
<do whatever>
e = requests.get(engines['bing'])
<do whatever>
Comments