Vraag Wat doet ** (dubbel sterretje / sterretje) en * (ster / sterretje) voor parameters?


In de volgende methodedefinities, wat doet het * en ** doen voor param2?

def foo(param1, *param2):
def bar(param1, **param2):

1574
2017-08-31 15:04


oorsprong


antwoorden:


De *args en **kwargs is een algemeen idioom om een ​​willekeurig aantal argumenten toe te staan ​​aan functies zoals beschreven in de sectie meer over het definiëren van functies in de documentatie van Python.

De *args zal je alle functieparameters geven als een tuple:

In [1]: def foo(*args):
   ...:     for a in args:
   ...:         print a
   ...:         
   ...:         

In [2]: foo(1)
1


In [4]: foo(1,2,3)
1
2
3

De **kwargs zal je alles geven zoekwoordargumenten behalve die welke overeenkomen met een formele parameter als een woordenboek.

In [5]: def bar(**kwargs):
   ...:     for a in kwargs:
   ...:         print a, kwargs[a]
   ...:         
   ...:         

In [6]: bar(name='one', age=27)
age 27
name one

Beide idiomen kunnen worden gecombineerd met normale argumenten om een ​​reeks vaste en enkele variabele argumenten toe te staan:

def foo(kind, *args, **kwargs):
   pass

Een ander gebruik van de *l idioom is om uitpakken argumentlijsten bij het aanroepen van een functie.

In [9]: def foo(bar, lee):
   ...:     print bar, lee
   ...:     
   ...:     

In [10]: l = [1,2]

In [11]: foo(*l)
1 2

In Python 3 is het mogelijk om te gebruiken *l aan de linkerkant van een opdracht (Extended Iterable Unpacking), hoewel het in deze context een lijst geeft in plaats van een tuple:

first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]

Ook voegt Python 3 nieuwe semantische toe (refer PEP 3102):

def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
    pass

Zo'n functie accepteert slechts 3 positionele argumenten, en alles daarna * kan alleen worden doorgegeven als zoekwoordargumenten.


1550
2017-08-31 15:17



Het is ook vermeldenswaard dat je kunt gebruiken * en ** bij het aanroepen van functies ook. Dit is een snelkoppeling waarmee u meerdere argumenten direct naar een functie kunt sturen met behulp van een lijst / tuple of een woordenboek. Als u bijvoorbeeld de volgende functie heeft:

def foo(x,y,z):
    print("x=" + str(x))
    print("y=" + str(y))
    print("z=" + str(z))

U kunt dingen doen als:

>>> mylist = [1,2,3]
>>> foo(*mylist)
x=1
y=2
z=3

>>> mydict = {'x':1,'y':2,'z':3}
>>> foo(**mydict)
x=1
y=2
z=3

>>> mytuple = (1, 2, 3)
>>> foo(*mytuple)
x=1
y=2
z=3

Opmerking: de toetsen in mydict moeten precies worden genoemd als de parameters van de functie foo. Anders gooit het een TypeError:

>>> mydict = {'x':1,'y':2,'z':3,'badnews':9}
>>> foo(**mydict)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() got an unexpected keyword argument 'badnews'

448
2017-08-31 15:47



De single * betekent dat er een aantal extra positioneringsargumenten kan zijn. foo() kan worden opgeroepen zoals foo(1,2,3,4,5). In de body van foo () is param2 een reeks die 2-5 bevat.

Het dubbele ** betekent dat er een willekeurig aantal extra genoemde parameters kan zijn. bar() kan worden opgeroepen zoals bar(1, a=2, b=3). In de body van bar () is param2 een woordenboek met {'a': 2, 'b': 3}

Met de volgende code:

def foo(param1, *param2):
    print param1
    print param2

def bar(param1, **param2):
    print param1
    print param2

foo(1,2,3,4,5)
bar(1,a=2,b=3)

de uitvoer is

1
(2, 3, 4, 5)
1
{'a': 2, 'b': 3}

128
2017-08-31 15:20



Wat doet ** (dubbele ster) en * (ster) doen voor parameters

Ze laten het toe functies die moeten worden gedefinieerd om te accepteren en voor gebruikers om te slagen een willekeurig aantal argumenten, positionele (*) en trefwoord (**).

Functies definiëren

*args staat een willekeurig aantal optionele positioneringsargumenten (parameters) toe, die worden toegewezen aan een tuple met de naam args.

**kwargs staat een willekeurig aantal optionele zoekwoordargumenten (parameters) toe, die in een dict worden genoemd kwargs.

U kunt (en zou) elke toepasselijke naam moeten kiezen, maar als het de bedoeling is dat de argumenten van niet-specifieke semantiek zijn, args en kwargs zijn standaardnamen.

Uitbreiding, een willekeurig aantal argumenten doorgeven

Je kan ook gebruiken *args en **kwargs om in parameters van respectievelijk lijsten (of elke iterabele) en dictaten (of elke toewijzing) door te geven.

De functie die de parameters ontvangt, hoeft niet te weten dat ze worden uitgebreid.

Het x-bereik van Python 2 verwacht bijvoorbeeld niet expliciet *args, maar omdat het 3 gehele getallen als argumenten kost:

>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x)    # expand here
xrange(0, 2, 2)

Als een ander voorbeeld kunnen we dict expansie gebruiken in str.format:

>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'

Nieuw in Python 3: functies definiëren met alleen zoekwoordargumenten

Je kan hebben keyword only arguments na de *args - hier bijvoorbeeld kwarg2 moet worden gegeven als zoekwoordargument - niet positioneel:

def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs): 
    return arg, kwarg, args, kwarg2, kwargs

Gebruik:

>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})

Ook, * kan alleen worden gebruikt om aan te geven dat alleen zoekwoordargumenten volgen, zonder rekening te houden met onbeperkte positioneringsargumenten.

def foo(arg, kwarg=None, *, kwarg2=None, **kwargs): 
    return arg, kwarg, kwarg2, kwargs

Hier, kwarg2 opnieuw moet een expliciet benoemd zoekwoordargument zijn:

>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})

En we kunnen niet langer onbeperkte positionele argumenten accepteren omdat we die niet hebben *args*:

>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes from 1 to 2 positional arguments 
    but 5 positional arguments (and 1 keyword-only argument) were given

Nogmaals, eenvoudiger, hier hebben we behoefte aan kwarg te geven op naam, niet positioneel:

def bar(*, kwarg=None): 
    return kwarg

In dit voorbeeld zien we dat als we proberen te slagen kwarg positioneel krijgen we een foutmelding:

>>> bar('kwarg')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bar() takes 0 positional arguments but 1 was given

We moeten expliciet de kwarg parameter als zoekwoordargument.

>>> bar(kwarg='kwarg')
'kwarg'

Python 2 compatibele demo's

*args (meestal "star-args" genoemd) en **kwargs (sterren kunnen worden geïmpliceerd door "kwargs" te zeggen, maar wees expliciet met "dubbele ster kwargs") zijn gangbare idiomen van Python voor het gebruik van de * en ** notatie. Deze specifieke variabelenamen zijn niet vereist (u zou bijvoorbeeld kunnen gebruiken *foos en **bars), maar een afwijking van de conventie zal je mede-Python-programmeurs waarschijnlijk irriteren.

We gebruiken deze meestal wanneer we niet weten wat onze functie gaat krijgen of hoeveel argumenten we doorgeven, en soms zelfs wanneer elke variabele afzonderlijk wordt benoemd, zou het erg rommelig en overbodig worden (maar dit is een geval waarin meestal expliciet wordt vermeld). beter dan impliciet).

voorbeeld 1

De volgende functie beschrijft hoe ze kunnen worden gebruikt en toont gedrag aan. Let op de benoemde b argument wordt gebruikt door het tweede positioneringsargument vóór:

def foo(a, b=10, *args, **kwargs):
    '''
    this function takes required argument a, not required keyword argument b
    and any number of unknown positional arguments and keyword arguments after
    '''
    print('a is a required argument, and its value is {0}'.format(a))
    print('b not required, its default value is 10, actual value: {0}'.format(b))
    # we can inspect the unknown arguments we were passed:
    #  - args:
    print('args is of type {0} and length {1}'.format(type(args), len(args)))
    for arg in args:
        print('unknown arg: {0}'.format(arg))
    #  - kwargs:
    print('kwargs is of type {0} and length {1}'.format(type(kwargs),
                                                        len(kwargs)))
    for kw, arg in kwargs.items():
        print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg))
    # But we don't have to know anything about them 
    # to pass them to other functions.
    print('Args or kwargs can be passed without knowing what they are.')
    # max can take two or more positional args: max(a, b, c...)
    print('e.g. max(a, b, *args) \n{0}'.format(
      max(a, b, *args))) 
    kweg = 'dict({0})'.format( # named args same as unknown kwargs
      ', '.join('{k}={v}'.format(k=k, v=v) 
                             for k, v in sorted(kwargs.items())))
    print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format(
      dict(**kwargs), kweg=kweg))

We kunnen de online help voor de handtekening van de functie controleren met help(foo), wat ons vertelt

foo(a, b=10, *args, **kwargs)

Laten we deze functie met noemen foo(1, 2, 3, 4, e=5, f=6, g=7) 

welke prints:

a is a required argument, and its value is 1
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type <type 'dict'> and length 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: g, arg: 7
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
4
e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns: 
{'e': 5, 'g': 7, 'f': 6}

Voorbeeld 2

We kunnen het ook met een andere functie noemen, die we zojuist hebben gegeven a:

def bar(a):
    b, c, d, e, f = 2, 3, 4, 5, 6
    # dumping every local variable into foo as a keyword argument 
    # by expanding the locals dict:
    foo(**locals()) 

bar(100) prints:

a is a required argument, and its value is 100
b not required, its default value is 10, actual value: 2
args is of type <type 'tuple'> and length 0
kwargs is of type <type 'dict'> and length 4
unknown kwarg - kw: c, arg: 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: d, arg: 4
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
100
e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns: 
{'c': 3, 'e': 5, 'd': 4, 'f': 6}

Voorbeeld 3: praktisch gebruik in decorateurs

OK, dus misschien zien we de utility nog niet. Stel je dus voor dat je verschillende functies hebt met redundante code voor en / of na de differentiatiecode. De volgende benoemde functies zijn slechts pseudo-code voor illustratieve doeleinden.

def foo(a, b, c, d=0, e=100):
    # imagine this is much more code than a simple function call
    preprocess() 
    differentiating_process_foo(a,b,c,d,e)
    # imagine this is much more code than a simple function call
    postprocess()

def bar(a, b, c=None, d=0, e=100, f=None):
    preprocess()
    differentiating_process_bar(a,b,c,d,e,f)
    postprocess()

def baz(a, b, c, d, e, f):
    ... and so on

We kunnen dit misschien anders aanpakken, maar we kunnen de overtolligheid zeker extraheren met een binnenhuisarchitect, en ons onderstaande voorbeeld laat zien hoe *args en **kwargs kan erg handig zijn:

def decorator(function):
    '''function to wrap other functions with a pre- and postprocess'''
    @functools.wraps(function) # applies module, name, and docstring to wrapper
    def wrapper(*args, **kwargs):
        # again, imagine this is complicated, but we only write it once!
        preprocess()
        function(*args, **kwargs)
        postprocess()
    return wrapper

En nu kan elke ingepakte functie veel bondiger worden geschreven, omdat we de overtolligheid hebben weggenomen:

@decorator
def foo(a, b, c, d=0, e=100):
    differentiating_process_foo(a,b,c,d,e)

@decorator
def bar(a, b, c=None, d=0, e=100, f=None):
    differentiating_process_bar(a,b,c,d,e,f)

@decorator
def baz(a, b, c=None, d=0, e=100, f=None, g=None):
    differentiating_process_baz(a,b,c,d,e,f, g)

@decorator
def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None):
    differentiating_process_quux(a,b,c,d,e,f,g,h)

En door onze code in te delen, welke *args en **kwargs stelt ons in staat om te doen, we reduceren coderegels, verbeteren de leesbaarheid en onderhoudbaarheid en hebben de enige canonieke locaties voor de logica in ons programma. Als we een deel van deze structuur moeten wijzigen, hebben we één plaats om elke wijziging aan te brengen.


105
2017-10-14 16:34



Laten we eerst begrijpen wat positionele argumenten en zoekwoordargumenten zijn. Hieronder ziet u een voorbeeld van functiedefinitie met Positieve argumenten.

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(1,2,3)
#output:
1
2
3

Dit is dus een functiedefinitie met positionele argumenten. Je kunt het ook met keyword / named arguments noemen:

def test(a,b,c):
     print(a)
     print(b)
     print(c)

test(a=1,b=2,c=3)
#output:
1
2
3

Laten we nu een voorbeeld van functiedefinitie bestuderen met zoekwoordargumenten:

def test(a=0,b=0,c=0):
     print(a)
     print(b)
     print(c)
     print('-------------------------')

test(a=1,b=2,c=3)
#output :
1
2
3
-------------------------

Je kunt deze functie ook met positionele argumenten aanroepen:

def test(a=0,b=0,c=0):
    print(a)
    print(b)
    print(c)
    print('-------------------------')

test(1,2,3)
# output :
1
2
3
---------------------------------

Dus we kennen nu functiedefinities met positionele en trefwoordargumenten.

Laten we nu de operator '*' en '**' eens bestuderen.

Let op: deze operatoren kunnen op 2 gebieden worden gebruikt:

een) functieaanroep

b) functiedefinitie

Het gebruik van '*' operator en '**' operator in functieaanroep. 

Laten we meteen naar een voorbeeld gaan en het dan bespreken.

def sum(a,b):  #receive args from function calls as sum(1,2) or sum(a=1,b=2)
    print(a+b)

my_tuple = (1,2)
my_list = [1,2]
my_dict = {'a':1,'b':2}

# Let us unpack data structure of list or tuple or dict into arguments with help of '*' operator
sum(*my_tuple)   # becomes same as sum(1,2) after unpacking my_tuple with '*'
sum(*my_list)    # becomes same as sum(1,2) after unpacking my_list with  '*'
sum(**my_dict)   # becomes same as sum(a=1,b=2) after unpacking by '**' 

# output is 3 in all three calls to sum function.

Dus onthoud

wanneer de operator '*' of '**' wordt gebruikt in een functieaanroep -

'*' operator pakt gegevensstructuur zoals een lijst of tuple uit in argumenten die nodig zijn door functiedefinitie.

'**' operator pakt een woordenboek uit in argumenten die nodig zijn door functiedefinitie.

Laten we nu eens kijken naar het gebruik van de operator '*' in functiedefinitie. Voorbeeld:

def sum(*args): #pack the received positional args into data structure of tuple. after applying '*' - def sum((1,2,3,4))
    sum = 0
    for a in args:
        sum+=a
    print(sum)

sum(1,2,3,4)  #positional args sent to function sum
#output:
10

In functie definitie de operator '*' verpakt de ontvangen argumenten in een tuple.

Laten we nu een voorbeeld van '**' bekijken dat wordt gebruikt in functiedefinitie:

def sum(**args): #pack keyword args into datastructure of dict after applying '**' - def sum({a:1,b:2,c:3,d:4})
    sum=0
    for k,v in args.items():
        sum+=v
    print(sum)

sum(a=1,b=2,c=3,d=4) #positional args sent to function sum

In functie definitie De operator '**' verpakt de ontvangen argumenten in een woordenboek.

Dus onthoud:

In een functieaanroep de '*' uitpakt gegevensstructuur van tuple of lijst in positionele of trefwoordargumenten om te worden ontvangen door functiedefinitie.

In een functieaanroep de '**' uitpakt gegevensstructuur van woordenboek in positionele of trefwoordargumenten om te worden ontvangen door functiedefinitie.

In een functiedefinitie de '*' packs positionele argumenten in een tuple.

In een functiedefinitie de '**' packs zoekwoordargumenten in een woordenboek.


37
2018-01-20 11:40



* en ** speciaal gebruik in de lijst met functieargumenten. * impliceert dat het argument een lijst is en ** impliceert dat het argument is een woordenboek. Hierdoor kunnen functies een willekeurig aantal nemen argumenten


20
2017-09-11 04:33



Uit de documentatie van Python:

Als er meer positioneringsargumenten zijn dan formele slotmachines, wordt een uitzondering TypeError opgehaald, tenzij een formele parameter met de syntaxis "* identifier" aanwezig is; in dit geval ontvangt die formele parameter een tuple die de overtollige positioneringsargumenten bevat (of een lege tuple als er geen overtollige positioneringsargumenten zijn).

Als een zoekwoordargument niet overeenkomt met een formele parameternaam, wordt een uitzondering TypeError opgehaald, tenzij een formele parameter met de syntaxis "** -code" aanwezig is; in dit geval ontvangt die formele parameter een woordenboek met de overtollige trefwoordargumenten (met de sleutelwoorden als sleutels en de argumentwaarden als bijbehorende waarden), of een (nieuw) leeg woordenboek als er geen overtollige trefwoordargumenten zijn.


11
2017-08-31 15:07



Ik wil een voorbeeld geven dat anderen niet hebben genoemd

* kan ook a uitpakken generator

Een voorbeeld uit Python3 Document

x = [1, 2, 3]
y = [4, 5, 6]

unzip_x, unzip_y = zip(*zip(x, y))

unzip_x is [1, 2, 3], unzip_y is [4, 5, 6]

De zip () ontvangt meerdere iretable args en retourneert een generator.

zip(*zip(x,y)) -> zip((1, 4), (2, 5), (3, 6))

7
2017-11-08 16:50



In Python 3.5 kun je deze syntaxis ook gebruiken in list, dict, tuple, en set displays (ook wel letterlijk genoemd). Zien PEP 488: Aanvullende generalisaties voor uitpakken.

>>> (0, *range(1, 4), 5, *range(6, 8))
(0, 1, 2, 3, 5, 6, 7)
>>> [0, *range(1, 4), 5, *range(6, 8)]
[0, 1, 2, 3, 5, 6, 7]
>>> {0, *range(1, 4), 5, *range(6, 8)}
{0, 1, 2, 3, 5, 6, 7}
>>> d = {'one': 1, 'two': 2, 'three': 3}
>>> e = {'six': 6, 'seven': 7}
>>> {'zero': 0, **d, 'five': 5, **e}
{'five': 5, 'seven': 7, 'two': 2, 'one': 1, 'three': 3, 'six': 6, 'zero': 0}

Het maakt het ook mogelijk meerdere iterables uit te pakken in een enkele functieaanroep.

>>> range(*[1, 10], *[2])
range(1, 10, 2)

(Met dank aan mgilson voor de PEP-link.)


6
2017-12-08 21:38



Hoewel het gebruik voor de ster / splat-operators is geweest uitgebreid in Python 3 vind ik de volgende tabel leuk, omdat deze betrekking heeft op het gebruik van deze operatoren met functies. De splat-operator (s) kan zowel binnen de functie worden gebruikt bouw en in de functie telefoontje:

            In function *construction*      In function *call*
=======================================================================
          |  def f(*args):                 |  def f(a, b):
*args     |      for arg in args:          |      return a + b
          |          print(arg)            |  args = (1, 2)
          |  f(1, 2)                       |  f(*args)
----------|--------------------------------|---------------------------
          |  def f(a, b):                  |  def f(a, b):
**kwargs  |      return a + b              |      return a + b
          |  def g(**kwargs):              |  kwargs = dict(a=1, b=2)
          |      return f(**kwargs)        |  f(**kwargs)
          |  g(a=1, b=2)                   |
-----------------------------------------------------------------------

Dit dient eigenlijk alleen om Lorin Hochstein's samen te vatten antwoord maar ik vind het nuttig.


6
2017-11-30 18:28



Naast functieaanroepen, zijn * args en ** kwargs handig in klassenhiërarchieën en hoeven ze ook niet te hoeven schrijven __init__ methode in Python. Een vergelijkbaar gebruik kan worden gezien in frameworks zoals Django-code.

Bijvoorbeeld,

def __init__(self, *args, **kwargs):
    for attribute_name, value in zip(self._expected_attributes, args):
        setattr(self, attribute_name, value)
        if kwargs.has_key(attribute_name):
            kwargs.pop(attribute_name)

    for attribute_name in kwargs.viewkeys():
        setattr(self, attribute_name, kwargs[attribute_name])

Een subklasse kan dan zijn

class RetailItem(Item):
    _expected_attributes = Item._expected_attributes + ['name', 'price', 'category', 'country_of_origin']

class FoodItem(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['expiry_date']

De subklasse wordt vervolgens geïnstantieerd als

food_item = FoodItem(name = 'Jam', 
                     price = 12.0, 
                     category = 'Foods', 
                     country_of_origin = 'US', 
                     expiry_date = datetime.datetime.now())

Ook kan een subklasse met een nieuw kenmerk dat alleen voor die subklasse-instantie zinvol is, de klasse Base aanroepen __init__ om de attributen-instelling te ontladen. Dit gebeurt via * args en ** kwargs. kwargs wordt voornamelijk gebruikt, zodat code leesbaar is met behulp van benoemde argumenten. Bijvoorbeeld,

class ElectronicAccessories(RetailItem):
    _expected_attributes = RetailItem._expected_attributes +  ['specifications']
    # Depend on args and kwargs to populate the data as needed.
    def __init__(self, specifications = None, *args, **kwargs):
        self.specifications = specifications  # Rest of attributes will make sense to parent class.
        super(ElectronicAccessories, self).__init__(*args, **kwargs)

welke kan worden geïnstatieerd als

usb_key = ElectronicAccessories(name = 'Sandisk', 
                                price = '$6.00', 
                                category = 'Electronics',
                                country_of_origin = 'CN',
                                specifications = '4GB USB 2.0/USB 3.0')

De volledige code is hier


3
2017-08-16 04:23