没有学不会的python
前言
前面一章节讲到了必须参数、默认参数、位置参数以及关键词参数,细述了它的方方面面以及注意事项,但是前面我为了方便大家理解,都是拆开来讲解各类参数的使用技巧的。实际开发中呢,往往是各类参数综合应用的情况居多,而综合使用与单独使用的情况有些不同。因此,接下来将继续讲解函数的综合应用。
函数的各类参数综合应用
在四类参数中,它们的优先级是必须参数>默认参数>位置参数>关键词参数。它们的一般表现形式如下:
def my_function(req_value,default_value="something",*args,**kwargs) pass
由于有优先级的存在,我们在使用的时候,要记住这些优先级,不然很可能在实际使用过程中,传递的参数值分配给了错误的参数变量。接下来举一些比较容易犯错的例子:
1、不同类型的参数有优先级,定义参数的时候要遵守优先级
第一种,必须参数写到了默认参数后面,错误示例如下:
def value_order(content="hello world", num, *args, **kwargs): print(num) print(content) print(args) print(kwargs)
这种情况其实还好,因为会报语法错误,即SyntaxError会有提示说必须参数不可以在默认参数后面。原文是:
SyntaxError: non-default argument follows default argument。我们只需要给必须参数和默认参数调换一个位置就行了。
第二种是,关键词参数写在位置参数前面,错误示例如下:
def value_order(num, content="hello world", **kwargs, *args): print(num) print(content) print(args) print(kwargs)
同样的,类似上面那种情况,编辑器也会提示说是语法错误,这个时候你只要更换一下位置参数和关键词参数的位置即可解决。
上面两种是初学者最容易犯的错误,记住了,不同类型的参数位置是不可以随便更改的。
2、自定义函数的参数不是必须的,可以只用一部分类型参数,也可以使用全部类型参数,同样可以一个也不用
很多初学者会认为函数必须要有参数,然后就发愁说不知道怎么定义参数。其实参数不是必须的,根据你的业务逻辑,参数可以有很多个,也可以只有一个,或者一个也没有。下面几种使用自定义函数的情况都是正确的:
def no_param(): """不需要传递参数""" pass def one_req_param(first_param): """必须传递一个参数""" pass def one_default_param(first_patam="hello"): """可以不传递参数,也可以传递一个参数覆盖默认值""" pass def other_param(fiest_param, second_param="hello"): """最少值传递一个参数,也可以传递两个,第二个参数值将覆盖hello这个默认值""" pass def no_sure_param(*args): """不确定要传多少参数,可用位置参数替代""" pass
3、传递参数也必须遵守优先级
不仅仅定义函数要注意参数类型的顺序,调用函数的时候,也必须遵守不同参数的优先级顺序。否则会发生意想不到的现象,严重的话会引起bug。
假设我们有以下这样的一个自定义函数:
def value_order(num, content="hello world", *args, **kwargs): print(num) print(content) print(args) print(kwargs)
上面这个函数,我们分别按照优先级顺序定义了四种类别的参数,那么我们传递参数值的时候就要注意了。下面有几种错误的用法:
第一种,直接跳过默认参数,分配值给位置参数。
调用示例:
value_order(100, 3, 4, 5, one=1)
上述代码,我们试图给num分配100,content使用默认值,然后3、4、5都分配给位置参数,one=1分配给关键词参数。但实际上,我们这种跳过默认参数直接分配值得用法是不正确的。它会产生这样的结果:
100 3 (4, 5) {'one': 1}
通过结果可以发现,content居然被分配了3,args分类了(4,5),这与我们的预期不符合。正确的操作应该是这样的。
value_order(100, "hello world", 3, 4, 5, one=1)
第二种,误认为参数必须传递完整。比如调用上面的自定义函数,可以有如下几种用法:
value_order(100, "hello world", 3, 4, 5, one=1) value_order(100) value_order(100, content="hello world") value_order(100, None, None, one=1)
其实不止这几种用法,除了num是必须传递的参数外,其它都是可以选择的。
第三种,如果有默认参数和位置参数同时需要传递值,请记得,默认参数不要用key=value的形式传递,而是直接传递值就行。
还是用前面的自定义函数,错误的示例如下:
value_order(100, content="hey", 3, 4, 5)
上面会报语法错误,因为编译器认为content="hey"是一个关键词参数,是传递给kwargs的,他不应该出现在args参数的前面。正确的用法应该是:
value_order(100, "hey", 3, 4, 5)
4、自定义函数的参数数量不要超过5个,如果超过了5个请检查函数时候可以进一步细分
这也是python编码规范的一种要求,如果参数数量过多,可以使用位置参数或者关键词参数代替。并且你需要考虑下,这个函数是否可以进一步划分。
5、复杂的参数必须给出文档说明,必要的时候还要给出使用范例
跟我们上一章讲到的文档说明一样,复杂的函数,尤其是可选参数,必须说明其作用、使用方法等。必要的时候还要给出一个使用范例来。很多有名气的python库就是这样用的,下面这段代码摘自requests库,说明了requests是怎么用的,还有各种参数的意义:
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 Usage:: >>> import requests >>> req = requests.request('GET', 'http://httpbin.org/get') <Response [200]> """ # By using the 'with' statement we are sure the session is closed, thus we # avoid leaving sockets open which can trigger a ResourceWarning in some # cases, and look like a memory leak in others. with sessions.Session() as session: return session.request(method=method, url=url, **kwargs)
函数的返回值
前面我们讲了很多示例,都是关于怎么定义函数,函数要点,以及怎么传递参数,怎么调用参数的。但是大家发现没有,前面讲的都是没有返回值的。什么是返回值呢?返回值就是被调用函数处理完代码逻辑后,将结果返回给被调用函数。返回值的关键词是return,下面我们实操一下。
最简单的返回值使用范例:
def get_sum(num1, num2): return num1 + num2 result = get_sum(1, 2) print(result)
上述代码将传递进去的两个参数进行求和运算,然后通过return返回,返回的结果就被赋值到了result里面了,也就是将运算结果反馈给主函数了。
返回值不仅可以返回一个值,也可以返回多个值。如果返回的是多个值,将会以元组的形式返回给主函数。
def get_sum(num1, num2, num3, num4): return num1 + num2, num3 + num4 result = get_sum(1, 2, 3, 4) result_one, result_two = get_sum(1, 2, 3, 4) print(result) print(result_one) print(result_two)
比如上面的函数,是分别求前两个数的和以及后两个数的和,然后将结果返回给主函数。如果我们调用子函数的时候,只将结果赋值给一个变量,那么这个变量就是一个元组,比如上面的result。但如果我们分别赋值给等量的变量,那么python自动会帮我们拆箱,分别桉顺序赋值给变量。比如result_one就是前两个数的和,result_two就是后两个数的和。
至此,我们以及完全掌握自定义函数的使用了,更多的细节和经验,需要大家多使用去发现,我这里只是讲了基本的使用法则。接下来我将会将lambda函数、python的函数式编程,喜欢的朋友可以关注我的主页也可以关注我的公众号。
有什么写的不好或者不对的地方,请留言,我会努力做的更好的。