6

I'm a few months into learning python. After going through pyramid tutorials I'm having trouble understanding a line in the init.py

from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from .models import (
    DBSession,
    Base,
    )



def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    config = Configurator(settings=settings)
    config.include('pyramid_chameleon')
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('home', '/')
    config.scan()
    return config.make_wsgi_app()

I'm lost about settings=settings in the configurator argument.

What is this telling python?

BScott
  • 135
  • 6
  • http://stackoverflow.com/questions/1769403/understanding-kwargs-in-python http://stackoverflow.com/questions/36901/what-does-double-star-and-star-do-for-python-parameters – Padraic Cunningham Dec 14 '15 at 23:19

4 Answers4

4

Python functions support keyword arguments:

def add(a, b):
    return a + b

add(a=1, b=2)

This happens here.

 Configurator(settings=settings)

The first settings is the name of the parameter in the __init__ of Configurator. The second is a name for an object in the current name space.

Mike Müller
  • 82,630
  • 20
  • 166
  • 161
2

Python supports calling any callable object (i.e. functions, constructors, or even objects understanding __call__ method) specifying positional arguments, named arguments, or even both types of arguments.

When you pass named arguments, they must be after the positional arguments (if any is passed).

So you can call any function, like:

def f(a, b):
    return a + b

In the following ways:

f(1, 2)
f(1, b=2)
f(a=1, b=2)
f(b=1, a=2) # Order doesn't matter among named arguments

While the following forms will trigger an error:

f(a=1, 2) # Named arguments must appear AFTER positional arguments
f(1, a=2) # You are passing the same argument twice: one by position, one by name

So, when you pass a named parameter, be sure you don't pass the same parameter twice (i.e. also by position), and also check that the argument name exists (also: if it is documented that the argument could/should be passed by name, respect their names on inheritance/override).

Additionally Python supports passing *arguments and **keyword_arguments. These are additional arguments you can process in a variadic way, as many languages support them.

*args (the name doesn't matter - it must have an asterisk to be a positional variadic; it's a tuple) holds remaining unmatched positional arguments (instead of getting a TypeError for positional argument being unexpected, such argument gets into *args as an element).

**kwargs (the name doesn't matter - it must have two asterisks to be a named/keyword variadic; it's a dictionary) holds the remaining unmatched named arguments (instead of getting a TypeError for named argument being unexpected, such argument gets into **kwargs as an element).

So, perhaps you see a function like this:

def f(*args, **kwargs):
    ...

You can call it with any parameters you want:

f(1, 2, 3, a=4, b=5, c=6)

Just keep the order: named arguments come after positional arguments.

You can declare a function like this:

f(m1, m2, ..., o1=1, o2=2, ..., *args, **kwargs):
    pass

Understanding the following:

  • m1, m2, ... are mandatory: when you call, you must fill them either positionally or respecting their name.
  • o1, o2, ... are optional: when you call, you can omit such parameters (omitting them implies not passing them by position nor by name), and they will hold the value after the equal sign (such value is evaluated when the function is declared - avoid using mutable objects as their values).
  • args and kwargs are what I explained you before: Any unmatched argument by position and by name go into these parameters. With these features, you will have a clear distinction between what is a parameter and what is an argument.
  • All these parameters are optional. You can choose not to use mandatory and only optionals. You can choose not to use *args but yes **kwargs, and so. But respect the order in the declaration: mandatory, optional, *args, **kwargs.

When you call the method and pass the arguments the semantic is very different so be wary:

f(1) # passes a positional argument. Has nothing to do with the parameter being mandatory.
f(a=1) # passes a named argument. Has nothing to do with the parameter being optional.
f(**i) # UNPACKS the positional arguments. Has nothing to do with the function having a *args parameter, but *args will hold any unpacked -but unmatched- positional argument from i (which is any type of sequence or generator)
f(**d) # UNPACKS its values as named arguments. Has nothing to do with the function having a **kwargs parameter, but **kwargs will hold any unpacked -but unmatched- argument from d (which is a dict having string keys).

When you make the call, you can pass them as you like, which has nothing to do with the actual method signature (i.e. expected parameters), but respect the order as with parameters: positional, named, *positionalUnpack, **keywordUnpack, or you will get a nice TypeError.

Examples:

def f(a, b=1, *args, **kwargs):
    pass

Valid calls:

f(1) # a = 1, b = 2, args = (), kwargs = {}
f(*[1]) #a = 1, b = 2, args = (), kwargs = {}
f(*[3, 4]) #a = 3, b = 4, args = (), kwargs = {}

f(**{'a':1, 'b':3}) #a = 1, b=3, args = (), kwargs = {}
f(1, *[2, 3, 4], **{'c': 5}) #a = 1, b=2, args=(3, 4), kwargs = {'c': 5}

Again beware:

  • Do not pass the same parameter twice (a clash would occur between **unpacked arguments with other named arguments, or positional arguments, or *unpacked arguments).
  • If you want to pass *args or **kwargs to a super call, ensure using the unpack syntax:

    def my_method(self, a, b, *args, **kwargs):
        super(MyClass, self).my_method(a+1, b+1, *args, **kwargs)
    
Luis Masuelli
  • 12,079
  • 10
  • 49
  • 87
1

That means that you are passing the argument settings to Configurator which contains a variable called settings

here an example, you have a function:

def function_test(a=None, b=None, c=None):
    pass

You could call it like that:

c = "something"
function_test(c=c)

which mean you passed the variable c that you created as an argument for the parameter c in the function function_test

ddalu5
  • 401
  • 4
  • 17
1

It's saying that it is passing the local name settings as the argument named settings in the Configurator.

For a function or constructor call of the form x=y, the x is the argument name used locally on the function/constructor side, while y is the name on the caller side. They just happen to be the same name in this case.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271