# Generating strings dynamically in Python

I'm generating a URL (in string) that depends on 3 optional parameters, file, user and active.

From a base url: /hey I want to generate the endpoint, this means:

• If file is specificied, my desired output would is: /hey?file=example
• If file and user is specified, my desired output is: /hey?file=example&user=boo
• If user and active are specified, my desired output is: /hey?user=boo&active=1
• If no optional parameters are specified, my desired output is: /hey
• and so on with all the combinations...

My code, which is working correctly, is as follows (change the None's at the top if you want to test it):

file = None
user = None
active = 1

ep = "/hey"
isFirst = True

if file:
if isFirst:
ep+= "?file=" + file;
isFirst = False;
else: ep += "&file=" + file;

if user:
if isFirst:
ep+= "?user=" + user;
isFirst = False;
else: ep += "&user=" + user;

if active:
if isFirst:
ep+= "?active=" + str(active);
isFirst = False;
else: ep += "&active=" + str(active);

print ep


Can someone give me a more python implementation for this? I can't use modules as requests.

• Lose the ;. makes python look ugly – hjpotter92 Dec 17 '18 at 14:00

The most Pythonic version of this depends a bit on what you do with that URL afterwards. If you are using the requests module (which you probably should), this is already built-in by specifying the params keyword:

import requests

URL = "https://example.com/hey"

r1 = requests.get(URL, params={"file": "example"})
print(r1.url)
# https://example.com/hey?file=example

r2 = requests.get(URL, params={"file": "example", "user": "boo"})
print(r2.url)
# https://example.com/hey?file=example&user=boo

r3 = requests.get(URL, params={"user": "boo", "active": 1})
print(r3.url)
# https://example.com/hey?user=boo&active=1

r4 = requests.get(URL, params={})
print(r4.url)
# https://example.com/hey


If you do need a pure Python solution without any imports, this is what I would do:

def get_url(base_url, **kwargs):
if not kwargs:
return base_url
params = "&".join(f"{key}={value}" for key, value in kwargs.items())
return base_url + "?" + params


Of course this does not urlencode the keys and values and may therefore be a security risk or fail unexpectedly, but neither does your code.

Example usage:

print(get_url("/hey", file="example"))
# /hey?file=example

print(get_url("/hey", file="example", user="boo"))
# /hey?file=example&user=boo

print(get_url("/hey", user="boo", active=1))
# /hey?user=boo&active=1

print(get_url("/hey"))
# /hey

• Due to the implementation of the rest of the code, I need to do it everything without any requests module, just improving the code I posted using strings. – Avión Dec 17 '18 at 10:09
• @Avión: Just did. It captures all keyword arguments you pass to the function into one dictionary. – Graipher Dec 17 '18 at 10:13
• Your code is good for illustrative purposes but it fails to URLencode the parameters and is therefore a potential security risk. – Konrad Rudolph Dec 17 '18 at 14:35
• @KonradRudolph Added a short disclaimer regarding that. – Graipher Dec 17 '18 at 14:38
• It's not just that it's a security risk, it's also that if you have & in one of the values, it will fail to send the correct value (and most likely fail in general, unless there's also another = in the values). – ChatterOne Dec 18 '18 at 8:33

You're pretty much reinventing urllib.parse.urlencode:

from urllib.parse import urlencode

def prepare_query_string(**kwargs):
return urlencode([(key, value) for key, value in kwargs.items() if value is not None])


Usage being:

>>> prepare_query_string(active=1)
'active=1'
>>> prepare_query_string(active=1, user=None)
'active=1'
>>> prepare_query_string(active=1, user='bob')
'active=1&user=bob'
>>> prepare_query_string(file='foo.tar.gz', user='bob')
'file=foo.tar.gz&user=bob'
>>> prepare_query_string(file='foo.tar.gz', user='bob', active=None)
'file=foo.tar.gz&user=bob'
>>> prepare_query_string(file='foo.tar.gz', user='bob', active=1)
'file=foo.tar.gz&user=bob&active=1'