Правило 5 - Компиляция без оператора присваивания [дубликат]

Что делает ** (двойная звезда) и * (звезда) для параметров

Они позволяют определять функции для принятия и для пользователей проходить любое число аргументов, positional (*) и ключевое слово (**).

Определяющие функции

*args допускают любое количество необязательных позиционных аргументов (параметров), который будет присвоен кортежу с именем args.

**kwargs допускает любое количество необязательных аргументов (параметров) ключевого слова, которые будут находиться в имени dict kwargs.

Вы можете (и должны) выбрать любое подходящее имя , но если целью является аргумент неспецифической семантики, args и kwargs являются стандартными именами.

Расширение, Передача любого количества аргументов

Вы также могут использовать *args и **kwargs для передачи параметров из списков (или любого итеративного) и dicts (или любого сопоставления) соответственно.

Функция, получающая параметры, не должна знать, что они расширяются.

Например, xrange Python 2 явно не ожидает *args, но поскольку в качестве аргументов он принимает 3 целых числа:

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

В качестве другого примера мы можем использовать расширение dict в str.format:

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

Новое в Python 3: Определение функций только с ключевыми аргументами

После [f0] ключевого слова можно использовать только - например, здесь kwarg2 должен быть задан как аргумент ключевого слова - не позиционно:

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

Использование:

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

Также можно использовать *

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

Здесь kwarg2 снова должен быть явно именованным аргументом ключевого слова:

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

И мы больше не можем принимать неограниченные позиционные аргументы, потому что у нас нет *args*:

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

Опять же, здесь мы требуем, чтобы kwarg указывался по имени, а не по положению :

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

В этом примере мы видим, что если мы попытаемся передать kwarg позиционно, мы получим ошибку:

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

We должен явно передать параметр kwarg в качестве аргумента ключевого слова.

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

Совместимые с Python демки

*args (обычно называемые «star-args») и **kwargs (звезды могут подразумеваться, говоря «kwargs», но быть явным с «двойными звездами») являются общими идиомами Python для использования нот * и **. Эти конкретные имена переменных не требуются (например, вы могли бы использовать *foos и **bars), но отклонение от конвенции может вызвать возмущение у ваших коллег-программистов Python.

Обычно мы используем их, когда мы не знаем, что получит наша функция, или сколько аргументов мы можем передавать, а иногда даже при наименовании каждой переменной отдельно получилось бы очень грязное и избыточное (но это это случай, когда обычно явный лучше, чем неявный).

Пример 1

Следующая функция описывает, как их можно использовать и демонстрирует поведение. Обратите внимание, что именованный аргумент b будет потребляться вторым аргументом position before:

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))

Мы можем проверить онлайн-справку для подписи функции с помощью help(foo), которая сообщает нам

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

Назовем эту функцию с помощью foo(1, 2, 3, 4, e=5, f=6, g=7)

, который печатает:

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  and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type  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}

Пример 2

Мы также можем назвать это с помощью другого функция, в которую мы просто предоставляем 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  and length 0
kwargs is of type  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}

Пример 3: практическое использование в декораторах

Хорошо, так что, возможно, мы еще не видим утилиту. Итак, представьте, что у вас есть несколько функций с избыточным кодом до и / или после дифференцирующего кода. Следующие именованные функции являются просто псевдокодом для иллюстративных целей.

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

Мы могли бы обрабатывать это по-другому, но мы можем, конечно, извлечь избыточность с помощью декоратора, и поэтому наш приведенный ниже пример демонстрирует, как *args и **kwargs могут быть очень полезными:

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

И теперь каждая завернутая функция может быть написана гораздо более лаконично, поскольку мы учли избыточность:

@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)

И, разглаживая наш код, который позволяет нам делать *args и **kwargs, мы сокращаем строки кода, улучшаем читаемость и ремонтопригодность и имеем единственные канонические места для логики нашей программы. Если нам нужно изменить какую-либо часть этой структуры, у нас есть одно место, в которое можно внести каждое изменение.

3
задан Tim S. Van Haren 8 January 2010 в 14:47
поделиться

3 ответа

В первом тестовом случае оператор присваивания не используется. Он просто использует форму инициализации, называемую «инициализация копирования». При инициализации объекта инициализация кода не рассматривает явные конструкторы.

struct A {
  A();

  // explicit copy constructor
  explicit A(A const&);

  // explicit constructor
  explicit A(int);

  // non-explicit "converting" constructor
  A(char const*c);
};

A a;
A b = a; // fail
A b1(a); // succeeds, "direct initialization"

A c = 1; // fail, no converting constructor found
A d(1); // succeeds

A e = "hello"; // succeeds, converting constructor used

Инициализация копирования используется в тех случаях, которые соответствуют неявным преобразованиям, где явно не запускается преобразование, как при передаче аргумента функции и возврате из функции.

10
ответ дан Johannes Schaub - litb 25 August 2018 в 15:20
поделиться

Стандарт C ++ 8.5 / 12

Инициализация, возникающая при передаче аргументов, возврат функции, исключение (15.1), обработка исключения (15.3) и список инициализаторов, заключенных в фигурные скобки ( 8.5.1) называется копией-инициализацией и эквивалентна форме

T x = a;

Инициализация, возникающая в новых выражениях (5.3.4), static_cast выражениях (5.2.9), преобразования типов функциональных обозначений (5.2.3), а начальные и членные инициализаторы (12.6.2) называются прямой инициализацией и эквивалентны форме

T x(a);
3
ответ дан Andreas Brinck 25 August 2018 в 15:20
поделиться

Ваш первый набор соответствует стандарту C ++ и не связан с некоторой оптимизацией.

Раздел 12.8 ([class.copy]) стандарта C ++ дает аналогичный пример:

class X {
    // ...
public:
    X(int);
    X(const X&, int = 1);
};

X a(1);     // calls X(int);
X b(a, 0);  // calls X(const X&, int);
X c = b;    // calls X(const X&, int);

Последняя строка будет соответствовать вашему случаю.

2
ответ дан stakx 25 August 2018 в 15:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: