10

For a long time i have been trying to figure out what is the best way to pass flags to python functions. The most straightforward way is something like:

def func(data, flag1, flag2, flag3):
    ...

func(my_data, True, False, True)

This is really nice and concise, but incredibly hard to read, since the word "True" or "False" tells you nothing about what flag is being set, and you have to carefully count the arguments starting from the left. You can make them keyword arguments:

def func(data, flag1=False, flag2=False, flag3=False):
    ...

func(my_data, flag1=True, flag3=True)

but this is kind of redundant, since the "True" doesn't carry any meaning at all. I could pass it as a list:

func(mydata, ['flag1', 'flag3'])

or

func(mydata, [func.flag1, func.flag3])

But the first feels rather dirty, using strings as flags, and the second is still somewhat repetitive. Ideally i want to say something like:

func(my_data, flag1, flag3)

to pass flags to a function with minimal verbosity and redundancy. Is there any way to do something like this in python?

EDIT: I ended up going with:

func(mydata, flagA=1, flagB=1)

mostly for the reasons mentioned: compile-time checking (versus passing in strings), no namespace pollution (as opposed to using global "ENUM"s) and minimal boilerplate (=1 or =0 is only 2 characters, vs 5 or 6 for =True or =False). It also makes setting default values for the flags very easy:

def func(data, flagA=1, flagB=0, flagC=1):
    ...

which is far more clear and far more easy than jumping through hoops to extract and assign defaults to **kwarg-style flags. The flags are basically statically checked and very clear/clean to write. Now if only I could shave off the last two characters...

4
  • 1
    Why do you want/need to pass flags in the first place? Maybe you should rethink your interface... Commented Aug 12, 2011 at 5:56
  • The flags will be used to set bools in the data structure, which will then control future behavior of the functions that operate on the data structure. This is an entirely non-OO approach, but i chose it out of simplicity; regardless, even if i did do it OO-style, the behavioral changes are pretty subtle and probably not worth creating a whole family of classes to handle. Commented Aug 12, 2011 at 6:36
  • Maybe you could pass a (simpler) data structure containing the values then? A set of flags, or a dict mapping flag names to bools... Commented Aug 12, 2011 at 6:37
  • 1
    I'd recommend def func(data, *, flagA=1, flagB=0, flagC=1) which means that these are keyword-only arguments. That is, you can write func(x, flagB=1) but not func(x, 1) Commented Jan 27, 2013 at 7:33

6 Answers 6

5

you can define flag1 ...flagN as global variables, and define your function with func( *args)

FLAG1 = 1
FLAG2 = 2

def func(*args):
   pass

func(FLAG1, FLAG2)

By defining flags separately, instead of using string, you can avoid typos in flags' names and some headache when debugging

Sign up to request clarification or add additional context in comments.

Comments

5

I am missing the good old bitwise flags:

a = 1
b = 2
c = 4
d = 8

def func( data, flags ):
    print( ( flags & a) == a )
    print( ( flags & b) == b )
    print( ( flags & c) == c )
    print( ( flags & d) == d )

>>>> func("bla", a|c|d)
>>>> True
>>>> False
>>>> True
>>>> True

2 Comments

Thank you for showing this. I searched google for half an hour on "python bit flags".
For this you might be interested in enum.IntFlag. See the docs
4

Some Python standard libraries use this:

re.match(pattern, str, re.MULTILINE | re.IGNORECASE)

You can tweak this approach by using *args:

my.func(a, b, c, my.MULTLINE, my.IGNORECASE)

I would really recommend going with flag1=True:

  • it is readable
  • flag name is checked at compile time (unless **kwargs is used)
  • you can use flag=1 and flag=0 instead of True and False to reduce the noise
  • you can temporarily change LONG_FLAG_NAME_YOU_DONT_REMEMBER=True to False without retyping the long name when you will need to change back

Comments

3

With *args:

def some_func(data, *args):
    # do something
    return args # this is just to show how it works


>>> print some_func(12, 'test', 'test2', 'test3')
('test', 'test2', 'test3')

This is a good question to understand how *args and **kwargs work : *args and **kwargs?

Comments

3

What about flipping it over?

flag1, flag2, flag3, flag4, flag5, flag6 = range(6)

def func(enable=[], disable=[],
         enabled_by_default=[flag5, flag6]):
    enabled = set(enabled_by_default + enabled) - set(disabled)
    if flag1 in enabled:
        ...
    if flag2 in enabled:
        ...

func(enable = [flag1, flag2, flag3],
     disable = [flag6])

Comments

-1

I prefer functions without flags. Rather do this:

def func_flag1(arg):
    pass # something useful

def func_flag2(arg):
    pass # something for flag 2.

But "flagX" would actually be something meaningful like "do_X_with_option".

I prefer this because it makes it clearer, you keep the functions simpler (fewer bugs), and you don't have to carry some constants into other modules (in the case of flags actually being some kind of enumeration).

1 Comment

The problem is that my flags are prettymuch independent; thus what would take O(n) flags would take O(2^n) functions to emulate. I have 2 flags right now, which would take 4 functions, and it will quickly get out of hand for even 3 or 4 flags

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.