Preface
As we all know, Python supports passing keyword arguments to functions. For example, Python's built-in function max
accepts key
to determine how to obtain the basis for comparing two parameters
max({'v': 1}, {'v': 3}, {'v': 2}, key=lambda o: o['v']) # 返回值为{'v': 3}
It is certainly not a problem to customize a function that uses the feature of keyword parameters. For example, imitate the function string-equal
def string_equal(string1, string2, *, start1=None, end1=None, start2=None, end2=None):
if not start1:
start1 = 0
if not end1:
end1 = len(string1) - 1
if not start2:
start2 = 0
if not end2:
end2 = len(string2) - 1
return string1[start1:end1 + 1] == string2[start2:end2 + 1]
Then pass parameters to it in the form of keyword parameters
string_equal("Hello, world!", "ello", start1=1, end1=4) # 返回值为True
uphold the Zen of Python in I can even bells and whistles, the syntax to use keyword arguments There should be one-- and preferably only one --obvious way to do it.
idea, string1
and string2
mass participation
string_equal(string1='Goodbye, world!', string2='ello') # 返回值为False
But Yu doesn't conceal flaws, and Python's keyword parameters also have their shortcomings.
Python's shortcomings
The disadvantage of Python's keyword parameter feature is that the same parameter cannot be used at the same time:
- Has its own parameter name, and;
- Can be obtained from
**kwargs
,
Two forms exist in the parameter list.
For example, we all know that Python has a well-known third-party library called requests, which provides for the development initiate HTTP requests. The instance method request
its class requests.Session
has a parameter list of up to 16 parameters that people can't help but use Long Parameter List to reconstruct it. (You can move to the request
method document observe)
For ease of use, the author of requests intimately provides requests.request
, so that only a simple function call is required
requests.request('GET', 'http://example.com')
requests.request
function supports the requests.Session#request
(please allow me to borrow Ruby's writing for instance methods). All of this is done by declaring **kwargs
variables in the parameter list and passing parameters to the latter with the same syntax in the function body. Achieved. (You can move to request function source code observe)
The disadvantage of this is that requests.request
function loses a lot of information. To know kwargs
in 0611545904b6bf, you must:
- To know
requests.request
how torequests.Session#request
in parameter passing - willkwargs
fully expanded is passed in the simplest case; - Look
requests.Session#request
the parameter list ofmethod
to exclude 0611545904b71f andurl
which parameters are left.
If you want to requests.request
use their own parameter name in the parameter list (for example params
, data
, json
, etc.), then calls requests.Session#request
becomes cumbersome up, it had to be written
with sessions.Session() as session:
return session.request(method=method, url=url, params=params, data=data, json=data, **kwargs)
The form of human beings is indeed a repeater.
For an elegant solution, you can refer to Common Lisp next door.
The advantages of Common Lisp
Common Lisp first appeared in 1984, 7 years before Python's 1991. But it is understood features of Python keyword arguments borrowed from Modula-3, rather than all origins Lisp. The keyword parameter characteristics in Common Lisp are quite different from those in Python. For example, according to the official Python manual, **kwargs
are only extra keyword parameters in 0611545904b7a1
If the form “**identifier” is present, it is initialized to a new ordered mapping receiving any excess keyword arguments
In Common Lisp, **kwargs
corresponds to &rest args
, which must be placed before the keyword parameter (ie on the left), and according to CLHS "A specifier for a rest parameter" , args
contains all unprocessed The parameter-also contains the keyword parameter after it
(defun foobar (&rest args &key k1 k2)
(list args k1 k2))
(foobar :k1 1 :k2 3) ;; 返回值为((:K1 1 :K2 3) 1 3)
If I have another function with foobar
, then I can easily pass all the parameters to it
(defun foobaz (a &rest args &key k1 k2)
(declare (ignorable k1 k2))
(cons a
(apply #'foobar args)))
(foobaz 1 :k1 2 :k2 3) ;; 返回值为(1 (:K1 2 :K2 3) 2 3)
Even if foobaz
are more keyword parameters supported foobar
, it can be handled easily, because Common Lisp supports passing a special keyword parameter :allow-other-keys
to the called function.
(defun foobaz (a &rest args &key k1 k2 my-key)
(declare (ignorable k1 k2))
(format t "my-key is ~S~%" my-key)
(cons a
(apply #'foobar :allow-other-keys t args)))
(foobaz 1 :k1 2 :k2 3 :my-key 4) ;; 打印my-key is 4,并返回(1 (:ALLOW-OTHER-KEYS T :K1 2 :K2 3 :MY-KEY 4) 2 3)
Back to the HTTP client example. In Common Lisp, I generally use drakma this third-party libraries to initiate HTTP requests, it derived a http-request
function, usage and requests.request
almost
(drakma:http-request "http://example.com" :method :get)
If I want to encapsulate a function http-get
that makes GET requests conveniently based on it, I can write it like this
(defun http-get (uri &rest args)
(apply #'drakma:http-request uri :method :get args))
If I want to directly expose part of the keyword parameters supported by http-get
in the parameter list of http-request
(defun http-get (uri &rest args &key content)
(declare (ignorable content))
(apply #'drakma:http-request uri :method :get args))
Furthermore, if I want to http-get
support parsing Content-Type
is application/json
response to the results, it can also be written
(ql:quickload 'jonathan)
(ql:quickload 'str)
(defun http-get (uri &rest args &key content (decode-json t))
;; http-request并不支持decode-json这个参数,但依然可以将整个args传给它。
(declare (ignorable content))
(multiple-value-bind (bytes code headers)
(apply #'drakma:http-request uri
:allow-other-keys t
:method :get
args)
(declare (ignorable code))
(let ((content-type (cdr (assoc :content-type headers)))
(text (flexi-streams:octets-to-string bytes)))
(if (and decode-json
(str:starts-with-p "application/json" content-type))
(jonathan:parse text)
text))))
As Dio Common Lisp, it easily did what we couldn't do.
Digression
Once upon a time, Python programmers would still talk about There should be one-- and preferably only one --obvious way to do it.
in the Zen of Python, but in fact, Python has a variety of ways to write the parameters of a function. Even in the process of writing this article, I learned that in the original Python parameter list, you can write /
to make the parameters on the left side become positional-only parameters.
def foo1(a, b): pass
def foo2(a, /, b): pass
foo1(a=1, b=2)
foo2(a=1, b=2) # 会抛出异常,因为a只能按位置来传参。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。