Python的函数和参数

2021-09-06  本文已影响0人  东方胖
image.png

实参和形参

parameter 是函数定义的参数形式
argument 是函数调用时传入的参数实体。

python的实参传参模式有哪些

对于函数调用的传参模式,一般有两种:

func(a, b)  # a, b都是位置传参,这是初级调用最为常见的传参模式
func(*(a, b)) #这也是位置传参,等于上面的例子 ,实践中比较少见,一般为了强调a,b有某种聚集关系可以这样传递
"Welcome {name}!".format(name='Xiaoming') 

此外,

func(key1=value1, key2=value2,...)
func(**{"key1": value1, "key2":value2})

也是关键字传参

python的四种形参定义方式

python的函数参数定义一般来说有五种: 位置和关键字参数混合仅位置参数仅关键字参数可变位置参数可变关键字参数。其中仅位置参数的方式仅仅是一个概念,python语法中暂时没有这样的设计。
通常我们见到的函数是位置和关键字混合的方式。

def func(foo, bar=None):
    ...
func(foo=f, bar="abc") # 关键字调用
func(f, bar="abc") #关键字和位置混合
func(f, "abc") #位置调用
def my_func(arg1, arg2):
      ...
# 调用
my_func(arg1, arg2="abc")
my_func(arg1, arg2)

既可以用关键字又可以用位置调用

func(arg, *, kw_only1, kw_only2):
      ... 

def func(*, name, age):
    print(name, age)

# 调用
func(name='Tom', age=100)  # 输出 Tom 100 
func(**{'name': 'Tom', 'age'=100}) # 输出 Tom 100

这种方式的定义只能使用关键字传参的模式

def func(*args):
      print(args) 

# 调用
func(1) # 输出 (1,)
func(1,2,3) #输出(1,2,3)
func(*[1,2,3]) #输出 (1,2,3)
func([1 , 2, 3]) #输出([1,2,3],) 注意二者的区别。

f(*some_list) 与 f(arg1, arg2, ...) (其中some_list = [arg1, arg2, ...])是等价的

def func(**kwargs):
      print(kwargs)

# 调用
func(name='XiaoMing') # 输出 {'name': 'XiaoMing'}
func(**{'name': 'XiaoMing'}) # 输出 {'name': 'XiaoMing'}
func(name='Zhangsan', age=20) # 输出 {'name': 'Zhangsan', 'age': 20}
func(**{'name': 'Zhangsan', 'age': 20}) # 输出 {'name': 'Zhangsan', 'age': 20}

网络模块request的request方法的设计
多数的可选参数被设计成可变关键字参数

def request(method, url, **kwargs):
    """Constructs and sends a :class:`Request <Request>`.

    :param method: method for the new :class:`Request` object.
    :param url: URL for the new :class:`Request` object.
    :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
    :param data: (optional) Dictionary or list of tuples ``[(key, value)]`` (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`.
    :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
    :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
    :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
    :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
        ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
        or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
        defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
        to add for the file.
    :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
    :param timeout: (optional) How many seconds to wait for the server to send data
        before giving up, as a float, or a :ref:`(connect timeout, read
        timeout) <timeouts>` tuple.
    :type timeout: float or tuple
    :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
    :type allow_redirects: bool
    :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
    :param verify: (optional) Either a boolean, in which case it controls whether we verify
            the server's TLS certificate, or a string, in which case it must be a path
            to a CA bundle to use. Defaults to ``True``.
    :param stream: (optional) if ``False``, the response content will be immediately downloaded.
    :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
    :return: :class:`Response <Response>` object
    :rtype: requests.Response

Good Design

关于函数输出:尽量使用返回值或元组的方式输出结果

有多种方法能够为函数定义输出:

class callByRef:
    def __init__(self, **args):
        for (key, value) in args.items():
            setattr(self, key, value)

def func4(args):
    args.a = 'new-value'        # args is a mutable callByRef
    args.b = args.b + 1         # change object in-place

args = callByRef(a='old-value', b=99)
func4(args)
print(args.a, args.b)

非常晦涩

关于默认参数:尽量不要用可变对象作为函数的默认参数

如果使用可变对象作为函数的默认参数,会导致默认参数在所有的函数调用中被共享。
例子1:

def addItem(item, data=['hello']):
      data.append(item)
      return data

addItem方法的data设计了一个默认参数,使用不当会造成默认参数被共享。
python里面,函数的默认参数被存在__default__属性中,这是一个元组类型
例子2:

def myfunc(a, arg1='hello', arg2='world'):
      arg1 = arg1 + arg2  # 左边的arg1实际上是一个新的对象,不会存入 \_\_default__属性中
print(myfunc.__default__)
# 输出 ('hello', 'world')

在例子1中,默认参数是一个列表,它是mutable的数据类型,当它写进 __defauts__属性中时,函数addItem的操作并不会改变它的id,相当于 __defauts__只是保存了data的引用,对于它的内存数据并不关心,每次调用addItem,都可以修改 addItem.__defauts__中的数据,它是一个共享数据。
如果默认参数是一个imutable类型,情况将会不一样,你无法改变默认参数第一次存入的值。

例子1中,连续调用addItem('world') 的结果会是

['hello', 'world']
['hello', 'world', 'world']
...

而不是期望的

['hello', 'world']
['hello', 'world']
编写函数的四个基本原则
  1. 装饰器
def mydeco(func):
      def new(*args, **kwargs):
            return func(*args, **kwargs)
  1. 函数的参数不确定
  2. 实现函数的多态,或在继承情况下需要调用父类的某些方法的时候
class A(object):
      def somefun(self, p1, p2):
            pass
 class B(A):
      def myfun(self, p3, *args, **kwargs):
           super.(B, self).somefunc(*args, **kwargs)
上一篇下一篇

猜你喜欢

热点阅读