Python参数类型以及常见的坑详解

导语

由于之前遇到过几次有关于参数类型的坑,以及经常容易把一些参数类型搞混淆,现在做一下有关参数类型的总结记录以及对之前踩坑经历的分析。

参数类型

首先我们列举一下有关于Python的参数类型,以及实际上的运用和原理。

  • 位置参数(必选参数)
  • 默认参数
  • 可变参数
  • 关键字参数

位置参数(必选参数)

首先是位置参数,同时也被称作必选参数,位置参数很好理解,只要记住这点:

在函数定义时直接给定的此参数名称,调用时按照参数的位置顺序,依次赋予参数值。

示例:

def person_info(name, age):
  print("My name is %s, I am %s years old" % (name, age))

person_info("zhangsan", "49")
# name,age都是位置参数,按照位置顺序,函数中依次接收参数值。

默认参数

默认参数,默认参数存在许多便利的地方,但是同时也存在许多坑,等到后面我们再去仔细分析下为什么存在这些坑,以下几点我们需要注意的:

  • 可以为一个或者多个参数指定默认值,当调用函数时可以不用传入该参数值,大大降低函数调用的难度。
  • 当需要用传入的参数值代替默认参数的默认值时,可以按照参数位置顺序传入,同时也可以指定参数名传入。

示例:

def person_info(name, age, sex='man'):
  print("My name is %s, I am %s years old, I am %s" % (name, age, sex))

person_info('zhangsan', '15')
person_info('lisi', '15', 'women')
person_info('lisi', '20', sex='women')

可变参数

可变参数,顾名思义就是传入的参数数量是可变的:

可变参数在实际中,传入的数量可以是任意多个,但也可以没有。

而可变参数会在传入函数内部时,是一个tuple的形式。

示例:

def add(*numbers):
  sum = 0
  for i in numbers:
    sum+=i
  return sum

print(add(1,3,4,2,1,4,1,3)) 

numbers=[2,3,4,1,5]
add(*numbers)
# 当传入的参数为list时,会将list中所有的元素作为可变参数,传进去

关键字参数

当可变参数在传入0个或者任意个参数时,这些可变参数会在函数调用时自动组装成一个tuple。而关键字参数也允许你传入0个或者任意个含参数名的参数,这些关键字参数会函数内部自动组装为一个dict。调用函数时,可以只传入必选参数。

扩展函数的功能,**kwargs

示例:

def person_info(**kw):
  for key,value in kw.items():
    print(key, value)

person_info(name='zhangsan', age=15)
person = {'name': 'zhangsan', 'age': 13}
person_info(**person)

命名关键字参数

对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。但是针对到底传入了哪些参数,就需要通过函数内部分析检查。所以命名关键字参数就是限制传入的参数的名字,只能传我已命名关键字参数。

  • 命名关键字参数需要一个特殊分隔符*,分隔符后面的参数会被视为命名关键字参数。
  • 当函数中已经存在一个可变参数,后面跟着的命名关键字参数就不需要一个*特殊分隔符——“”**。
  • 命名关键参数可以有默认值,从而简化调用。
  • 命名关键参数必须传入一个参数名,这和位置参数不同。如果没有传入参数名,调用将会报错。

示例:

def person_info(name, *, age, sex):
  print(name, age, sex)

def person_info2(name, *args, age, sex):
  for i in args:
    print(i)
  print(name, age, sex)

person_info('zhangsan', age=12, sex='man')
person_info2('zhangsan', 'sksks', 'ssk', age=13, sex='man') 

参数组合调用规则

在python定义函数过程中,可以用位置参数、默认参数、可变参数、关键字参数、命名关键字参数。这五种参数都可以通过组合使用。需要注意的是:

这五种参数定义的顺序必须是:位置参数、默认参数、可变参数、命名关键字参数、关键字参数。

位置参数和默认参数组合

def Person(name, age=20):
  print(name,age)

Person('zhangsan')
Person('zhangsan', 20)

位置参数、默认参数、可变参数组合

def Person(name, age=20, *args):
  for i in args:
    print(i)
  print(name, age)

Person('zhangsan')
Person('zhangsan', 22, "Beijing")
Person('zhangsan', age=22, 'Shanghai') 

位置参数、默认参数、可变参数、命名关键字参数组合

def Person(name, age=20, *args, city, **kwargs):
  for i in args:
    print(i)
  for key,value in kwargs.items():
    print(key, value)
  print(name, age, city)

Person('zhangsan', age=12, 'Author', city='Shanghai', company='Shanghai Software') 

关于参数定义的一些坑

默认参数陷阱

关于默认参数陷阱的问题,我们先来看一看一个示例:

def Book(book, book_list=[]):
  print(id(book_list))
  book_list.append(book)
  for book in book_list:
    print(book)
  print(id(book_list))  

test = Book("First One") 

输出的结果:

这个输出的结果应该是意料之中,现在我们这时候再调用Book()方法,看看会发生什么:

这时候输出结果,竟然把之前的First one都输出,看了他们的id,发现都是同一块内存地址,这时候就开始纳闷了,那么来找找出现这种状况的原因。

经过查阅官方资料发现,这是一段Python官方文档给出的解释:

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:

我们来看看解释分析下,Python官方文档给出的理由就是Python对默认值只计算一次,对于可变对象,在后续调用的情况下会累积传递给他们。而list、dict等这种都属于可变对象。

那么对于这种默认值陷阱,我们是该如何避免造成一些不必要的麻烦呢?大致有两种解决方法:

  • 避免使用可变对象作为默认值。
  • 在参数定义的时候可以使用None对象作为占位符。

对于第二种方法:

def Book(book, book_list=None):
  print(book_list)
  if book_list is None:
    book_list = []
  book_list.append(book)
  for book in book_list:
    print(book)
  print(id(book_list)) 

test1 = Book('First one')
test2 = Book('Second one') 

测试结果:

慎用变长参数

前面已经介绍过了,Python是支持可变长度的参数列表,可以在函数定义参数时使用*args和**kwargs两个特殊的语法来实现。

那为什么要说慎用变长参数,我总结了一下有以下几个原因:

使用过于灵活。比如在我上面有关不同类型参数组合使用的示例中,在位置参数和默认参数在的情况下,还有可变参数、关键字参数、命名关键字参数。这就很容易是的这个函数的签名不够清晰,调用者需要花费时间去了解你这个方法该如何调用。所以这就很容易使得团队开发中效率低效。

另外一个原因,如果一个函数的列表过于长,虽然可以通过使用*args, **kwargs来简化函数,但同时也意味这个函数或许有更好的实现方式,有重构的必要。

说完了要慎用,在说说看我们常用的变长参数的使用场景:

  • 为函数添加一个装饰器。
  • 如果参数的数目不确定的时候,可以考虑使用变长参数。比如读取一些配置文件中的配置项时。
  • 用来实现函数的多态,或者在继承情况下子类需要调用父类的某些方法。

总结

关于的Python参数类型就写到这里了,刚开始学Python的时候,经常被函数定义的参数类型搞懵,后面看了一些教程,自己在写一些脚本的时候遇到的一些坑,并且在看一些大牛分析背后的原理,后面感觉收获良多。后面干脆想把自己学习过程遇到的东西都整理一下,做个记录,加深理解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • python版本坑:md5例子(python2与python3中md5区别)

    起步 对于一些字符,python2和python3的md5加密出来是不一样的. # python2.7 pwd = "xxx" + chr(163) + "fj" checkcode = hashlib.md5(pwd).hexdigest() print checkcode # ea25a328180680aab82b2ef8c456b4ce # python3.6 pwd = "xxx" + chr(163) + "fj"

  • Python中实现参数类型检查的简单方法

    Python是一门弱类型语言,很多从C/C++转过来的朋友起初不是很适应.比如,在声明一个函数时,不能指定参数的类型.用C做类比,那就是所有参数都是void*类型!void类型强制转换在C++中被广泛地认为是个坏习惯,不到万不得已是不会使用的. Python自然没有类型强制转换一说了,因为它是动态语言.首先,所有对象都从Object继承而来,其次,它有强大的内省,如果调用某个不存在的方法会有异常抛出.大多数情况,我们都不需要做参数类型栓查,除了一些特殊情况.例如,某个函数接受一个str类型,结果

  • Python函数参数类型*、**的区别

    刚开始学习python,python相对于java确实要简洁易用得多.内存回收类似hotspot的可达性分析, 不可变对象也如同java得Integer类型,with函数类似新版本C++的特性,总体来说理解起来比较轻松.只是函数部分参数的"*"与"**",闭包等问题,着实令人迷糊了一把,弄清概念后写下此文记录下来,也希望本文能够帮助其他初学者. 所以本文是一篇学习笔记,着重于使用的细节和理解上,首先分别介绍了函数各种参数类型在调用和声明时的区别,及其在混用时需要注意

  • python通过装饰器检查函数参数数据类型的方法

    本文实例讲述了python通过装饰器检查函数参数数据类型的方法.分享给大家供大家参考.具体分析如下: 这段代码定义了一个python装饰器,通过此装饰器可以用来检查指定函数的参数是否是指定的类型,在定义函数时加入此装饰器可以非常清晰的检测函数参数的类型,非常方便 复制代码 代码如下: def accepts(exception,**types):     def check_accepts(f):         assert len(types) == f.func_code.co_argco

  • Python创建二维数组实例(关于list的一个小坑)

    0.目录 1.遇到的问题 2.创建二维数组的办法 •3.1 直接创建法 •3.2 列表生成式法 •3.3 使用模块numpy创建 1.遇到的问题 今天写Python代码的时候遇到了一个大坑,差点就耽误我交作业了... 问题是这样的,我需要创建一个二维数组,如下: m = n = 3 test = [[0] * m] * n print("test =", test) 输出结果如下: test = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] 是不是看起来没有一点问

  • python 判断参数为Nonetype类型或空的实例

    Nonetype和空值是不一致的,可以理解为Nonetype为不存在这个参数,空值表示参数存在,但是值为空 判断方式如下: if hostip is None: print "no hostip,is nonetype" elif hostip: print "hostip is not null" else: print " hostip is null" 以上这篇python 判断参数为Nonetype类型或空的实例就是小编分享给大家的全部内

  • 跟老齐学Python之坑爹的字符编码

    字符编码,在编程中,是一个让学习者比较郁闷的东西,比如一个str,如果都是英文,好说多了.但恰恰不是如此,中文是我们不得不用的.所以,哪怕是初学者,都要了解并能够解决字符编码问题. >>> name = '老齐' >>> name '\xe8\x80\x81\xe9\xbd\x90' 在你的编程中,你遇到过上面的情形吗?认识最下面一行打印出来的东西吗?看人家英文,就好多了 >>> name = "qiwsir" >>&g

  • Python参数类型以及常见的坑详解

    导语 由于之前遇到过几次有关于参数类型的坑,以及经常容易把一些参数类型搞混淆,现在做一下有关参数类型的总结记录以及对之前踩坑经历的分析. 参数类型 首先我们列举一下有关于Python的参数类型,以及实际上的运用和原理. 位置参数(必选参数) 默认参数 可变参数 关键字参数 位置参数(必选参数) 首先是位置参数,同时也被称作必选参数,位置参数很好理解,只要记住这点: 在函数定义时直接给定的此参数名称,调用时按照参数的位置顺序,依次赋予参数值. 示例: def person_info(name, a

  • Python 代码智能感知类型标注与特殊注释详解

    目录 一.代码智能感知 二.类型标注 函数返回值的类型标注 变量的类型标注 三.特殊的注释 四.特殊的类型 一个不会写好的类型标注和注释的Python程序员,是让使用TA的代码的人都痛苦无比的事情…… —— 某某大佬 一.代码智能感知 想必大部分现代的集成开发环境(IDE)都有代码智能感知功能吧! 智能感知(IntelliSense),就是在我们写代码的时候,代码编辑器自动弹出我们代码中需要补全的部分,而这些补全的部分就是代码编辑器通过智能感知得到的,最重要的是,代码编辑器智能地感知补全的部分是

  • Python命令行参数解析包argparse的使用详解

    目录 一.argparse简介 二.简单案例 三.ArgumentParser参数 四.add_argument指令参数解释 五.vars() 一.argparse简介 argparse 是 python 自带的命令行参数解析包,可以用来方便的服务命令行参数,使用之前需要先导入包 import argparse 二.简单案例 简单使用,创建一个名为test.py的文件 # 导入 argparse 模块 import argparse # 创建一个argparse 的对象 parser = arg

  • 对python中url参数编码与解码的实例详解

    一.简介 在python中url,对于中文等非ascii码字符,需要进行参数的编码与解码. 二.关键代码 1.url编码 对字符串编码用urllib.parse包下的quote(string, safe='/', encoding=None, errors=None)方法. 对json格式的参数名和值编码,用urllib.parse包下的 urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote_via=qu

  • 关于python导入模块import与常见的模块详解

    0.什么是python模块?干什么的用的? Java中如果使用abs()函数,则需要需要导入Math包,同样python也是封装的,因为python提供的函数太多,所以根据函数的功能将其封装在不同的module模块中.就这样的话,pthon提供的module还是海量的,所以除非使用某个模块里的某个函数时才会将其导入程序中.所以你使用某个函数前,要先知道他在哪个module里,然后将这个模块导入当前程序,然后才能调用这个模块里的函数. 当然 python的模块分为用户自定义的和系统提供的.Pyth

  • Python 带有参数的装饰器实例代码详解

    demo.py(装饰器,带参数的装饰器): def set_level(level_num): def set_func(func): def call_func(*args, **kwargs): if level_num == 1: print("----权限级别1,验证----") elif level_num == 2: print("----权限级别2,验证----") return func() return call_func return set_f

  • Python进阶_关于命名空间与作用域(详解)

    写在前面 如非特别说明,下文均基于Python3 命名空间与作用于跟名字的绑定相关性很大,可以结合另一篇介绍Python名字.对象及其绑定的文章. 1. 命名空间 1.1 什么是命名空间 Namespace命名空间,也称名字空间,是从名字到对象的映射.Python中,大部分的命名空间都是由字典来实现的,但是本文的不会涉及命名空间的实现.命名空间的一大作用是避免名字冲突: def fun1(): i = 1 def fun2(): i = 2 同一个模块中的两个函数中,两个同名名字i之间绝没有任何

  • Python使用base64模块进行二进制数据编码详解

    前言 昨天团队的学妹来问关于POP3协议的问题,所以今天稍稍研究了下POP3协议的格式和Python里面的poplib.而POP服务器往回传的数据里有一部分需要用到Base64进行解码,所以就顺便看了下Python里面的base64模块. 本篇先讲一下base64模块,该模块提供了关于Base16,Base32,Base64,Base85和Ascii85的编码和解码相关的函数.有关poplib模块的内容,会在后面发上来.嗯,又挖了一个坑,这辈子挖的坑填不完了... 以下内容摘自http://bb

  • Python用sndhdr模块识别音频格式详解

    本文主要介绍了Python编程中,用sndhdr模块识别音频格式的相关内容,具体如下. sndhdr模块 功能描述:sndhdr模块提供检测音频类型的接口. 唯一一个API sndhdr模块提供了sndhdr.what(filename)和sndhdr.whathdr(filename)两个函数.但实际上它们的功能是一样的.(不知道多写一个的意义何在,what函数在内部调用了whathdr函数并把数据完完整整地返回) 在之前的版本,whathdr函数返回元组类型的数据,在Python3.5版本之

  • Python中lru_cache的使用和实现详解

    在计算机软件领域,缓存(Cache)指的是将部分数据存储在内存中,以便下次能够更快地访问这些数据,这也是一个典型的用空间换时间的例子.一般用于缓存的内存空间是固定的,当有更多的数据需要缓存的时候,需要将已缓存的部分数据清除后再将新的缓存数据放进去.需要清除哪些数据,就涉及到了缓存置换的策略,LRU(Least Recently Used,最近最少使用)是很常见的一个,也是 Python 中提供的缓存置换策略. 下面我们通过一个简单的示例来看 Python 中的 lru_cache 是如何使用的.

随机推荐