诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技平台

频道:欧洲联赛 日期: 浏览:173

毫无疑问,函数是 Python 言语里最重要的概念之一。在编程时,咱们将实在国际里的大问题分解为小问题,然后经过一个个函诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技渠道数交出答案。函数便是重复代码的克星,也是对立代码复杂度的最佳兵器。

好像大部分故事都会有结局,绝大多数函数也都是以回来成果作为完毕交配马。函数回来成果的方法,决议了调用它时的体会。所以,了解怎么高雅的让函数回来成果,是编写好函数的必备常识。

Python 的函数回来方法

Python 函数经过调用 return 句子来回来成果。运用 returnvalue 能够回来单个值,用 returnvalue1,value2 则能让函数一起回来多个值。

假定一个函数体内没有任何 return 句子,那么这个函数的回来值默以为 None。除了经过 return句子回来内容,在函数内还能够运用抛出反常(raise Exception)的方法来“回来成果”。

接下来,我将罗列一些与函数回来相关的常用编程主张。

编程主张

1. 单个函数不要回来多种类型

Python 言语十分灵敏,咱们能用它轻松完结一些在其他言语里很难做到的作业。比方:让一个函数一起回来不同类庆阳张万福型的成果。然后完结一种看起来十分有用的“多功能函数”。

就像下面这样:

def
get_users(user_id=
None
):

if
user_id
is

None
:

return

User
.
get
(user_id)

else
:

return

User
.filter(is_active=
True
)
# 回来单个用户
get_users(user_id=
1
)
# 回来多个用户
get_users()

当咱们需求获取单个用户时,就传递 user_id 参数,不然就不传参数拿到全部活泼用诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技渠道户列表。全部都由一个函数 get_users 来搞定。这样的规划好像很合理。

然而在函数的国际里,以编写具有“多功能”的瑞士军刀型函数为荣不是一件功德。这是由于好的函数必定是 “单一责任(Single responsibility)” 的。单一责任意味着一个函数只做好一件事,意图清晰。这样的函数也更不简略在未来由于需求改变而被修正。

而回来多种类型的函数必定是违背“单一责任”准则的,好的函数应该总是供给安稳的回来值,把调用方的处理本钱降到最低。像上面的比方,咱们应该编写两个独立的函数 get_user_by_id(user_id)、 get_active_users()来代替。

2. 运用 partial 结构新函数

假定这么一个场景,在你的代码里有一个参数许多的函数 A,适用性很强。而另一个函数 B 则是彻底经过调用 A 来完结作业,是一种相似方便方法的存在。

比方在这个比方里, double 函数便是彻底经过 mul科琳卫浴tiply 来完结核算的:

def
multiply(x,诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技渠道 y):

return
x * y
def
double(value):

# 回来另一个函数调用成果

return
multiply(
2
, value)

关于上面这种场景,咱们能够运用 functools 模块里的 partia女人情感l() 函数来简化它。

partial(func,*args,**kwargs) 根据传入的函数与可变(方位/关键字)参数来结构一个新函数。全部对新函数的调用,都会在兼并了当时调用参数与结构参数后,署理给原始函数处理。

运用 partial 函数,上面的 double 函数界说能够被修正为单行表达式,更简练也更直接。

import
functools
double = functools.partial(multiply,
2
)

主张阅览:partial 函数官方文档

3. 抛出反常,而不是回来成果与过错

我在前面提过,Python 里的函数能够回来多个值。根据这个才能,咱们能够编写一类特别的函数:一起回来成果与过错信息的函数。

def
create_item(name):

if
len(name) > MAX_LENGTH_OF_NAME:

return

None
,
'name of item is too long'

if
len(CURRENT_ITEMS) > MAX_ITEMS_QUOTA:

return

None
,
'items is full'

return

Item
(name=name),
''
def
create_from_input():
name = input()
item, err_msg = create_item(name)

if
err_msg:

print
(f
'create item failed: {err_msg}'
)

else
:

print
(f
'item<{name}> created'
)

在示例中, create_item 函数的作用是创立新的 Item 方针。一起,为了在犯错时给调用方供给过错概况,它运用了多回来值特性,把过错信息作为第二个成果回来。

乍看上去,这样的做法很天然。尤其是对那些有 Go 言语编程经历的人来说更是如此。可是在 Python 国际里,这并非处理此类问题的最佳方法。由于这种做法会添加调用方进行过错处理的本钱,尤其是当许多函数都遵从这个规范并且存在多层调用时。

Python 具有完善的反常(Exception)机制,并且在某种程度上鼓舞咱们运用反常(官方文档关于 EAFP 的阐明)。所以,运用反常来进行过错流程处理才是更地道的做法。

引进自界说反常后,上面的代码能够被改写成这样:

class

Cr企迈云商eateItemError
(
Exception
):

"""创立 Item 失利时抛出的反常"""
def
create_item(name):

"""创立一个新的 Item
:raises: 当无法创立时抛出 CreateItemError
"""

if
len(name) > MAX_LENGTH_OF_NAME:

raise

CreateItemError
(
'name of item is too long'
)

if
len(CURRENT_ITEMS) > MAX_ITEMS_QUOTA:

raise

CreateItemError
(
'items is full'
)

return

Item
(name=name)
def
create_for_input():
name = input()

try
:
item = create_item(name)

except

CreateItemError

as
e:

print
(f
'create item failed: {err_msg}'
)

else
:

print
(f
'item<{name}> created'
)

运用“抛出反常”代替“回来 (成果, 过错信息)”后,整个过错流程处理乍看上去改变不大,但实际上有着十分多不同,一些细节:

  • 新版本函数具有更安稳的回来值类型,它永久只会回来 Item 类型或是抛出反常
  • 尽管我在这里鼓舞运用反常,但“反常”总是会无法防止的让人 感到惊奇,所以,最好在函数文档里阐明或许抛出的反常类型
  • 反常不同于回来值,它在被捕获前会不断往调用栈上层报告。所以 create_item 的一级调用方彻底能够省掉反常处理,交由上层嘉定月亮湾庄园处理。这个特色给了咱们更多的灵敏性,但一起也带来了更大的危险。

Hint:怎么在编程言语里处理过错,是一个至今依然存在争议的主题。比方像上面不引荐的多回来值方法,正是缺少反常的 Go 言语中最中心的过错处理机制。别的,即使是反常机制本身,不同编程言语之间也存在着不同。

反常,或是不反常,都是由言语规划者进行多方取舍后的成果,更多时分不存在绝对性的好坏之分。可是,单就 Python 言语而言,运用反常来表达过错无疑是更契合 Python 哲学,更应该遭到推重的。

4. 慎重运用 None 回来值

None 值一般被用来表明“某个应该存在可是缺失的东西”,它在 Python 里是绝无仅有的存在。许多编程言语里都有与 None 相似的规划,比方 JavaScript 里的 null、Go 里的 nil 等。由于 None 所具有的一起 虚无 气质,它经常被作为函数回来值运用。

当咱们运用 None 作为函数回来值时,一般是下面 3 种状况。

1. 作为操作类函数的默许回来值

当某个操作类函数不需求任何回来值时,一般就会回来 None。一起,None 也是不带任何 return 句子函数的默许回来值。

关于这种函数,运用 None 是没有任何问题的,规范库里的 list.append()、 os.chdir() 均属此类。

2. 作为某些“预料之中”的或许没有的值

有一些函数,它们的意图一般是去尝试性的做某件作业。视状况不同,终究或许有成果,也或许没有成果。而对调用方来说,“没有成果”彻底是预料之中的作业。对这类函数来说,运用 None 作为“没成果”时的回来值也是合理的。

在 Python 规范库里,正则表达式模块 re 下的 re.search、 re.match 函数均属于此类,这两个函数在能够找到匹配成果时回来 re.Match 方针,找不到时则回来 None。

3. 作为调用失利时代表“过错成果”的值

有时, None 也会经常被咱们用来作为函数调用失利时的默许回来值,比方下面这个函数:

def
create_user_from_name(username):

"""经过用户名创立一个 User 实例"""

if
validate_username(username):

return

User
.from_username(username)

else
:

return

None
user = create_user_from_诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技渠道name(username)
if
user:
user.do_something()

当 username 不合法时,函数 create_user_from_name 将会回来 None。但在这个场景下,这样做其实并不好。

不过你或许会觉得这个函数彻底入情入理,乃至你会觉得它和咱们说到的上一个“没有成果”时的用法十分相似。那么怎么区别这两种不同景象呢?关键在于:函数签名(称号与参数)与 None 回来值之间是否存在一种“预料之中”罪恶都市阳光车行使命的暗示。

让我解释一下,每逢你让函数回来 None 值时,请仔细阅览函数名,然后问自己一个问题:假定我是该函数的运用者,从这个姓名来看,“拿不到任何成果”是否是该函数称号意义里的一部分?

分别用这两个函数来举例:

  • re.search():从函数名来看, search,代表着从方针字符串里去查找匹配成果,而查找行为,一向是或许有也或许没有成果的,所以该函数合适回来 None
  • create_user_from_name():从函数名来看,代表根据一个姓名来构建用户,并不能读出一种 或许回来、或许不回来的意义。所以不合适回来 None

关于那些不能从函数名里读出 None 值暗示的函数来说,有两种修正方法。第一种,假定你坚持运用 None 回来值,那么请修正函数的称号。比方能够将函数 create_user_from_name() 改名为 create_user_老街张婉清or_none()。

第二种方法则更常见的多:用抛出反常(raise Exception)来代替 None 回来值。由于,假定回来不了正常成果并非函数意义里的一部分,这就代表着函数呈现了“预料以外的状青蓝金服况”,而这正是Exceptions 反常 所掌管的范畴。

运用反常改写后的比方:

class

UnableToCreateUser
(
Exception
):

""魔界骑士英格丽德"当无法创立用户时抛出"""
def
create_user_from_name(username):

""
经过用户名创立一个
User
实例
"
:raises: 当无法创立用户时抛出 UnableToCreateUser
"""

if
validate_username(username):

return

User
.from_username(username)

else
:

raise

UnableToCreateUser
(f
'unable to create user from {username}'
)
try
:
user = create_user_from_name(username)
except

UnableToCreateUser
:

# Error handling
else
:
user.do_something()

与 None 回来值比较,抛出反常除了具有咱们在上个场景说到的那些特色外,还有一个额定的优势:能够在反常信息里供给呈现预料之外成果的原因,这是只回来一个 None 值做不到的。

5. 匂宫出梦合理运用“空方针形式”

我在前面说到函数能够用 None 值或反常来回来过错成果,但这两种方法都有一个一起的缺陷。那便是全部需求运用函数回来值的当地,都必须加上一个 if 或 鞍山家讯房产网try/except 防护句子,来判别成果是否正常。

让咱们看一个可运转的完好示例:

import
decimal
class

CreateAccountError
(
Exception
):

"""Unable to create a account error"""
class

Account
:

"""一个虚拟的银行账号"""

def
__init__(self, username, balance):
self.username = username
self.balance = balance

@classmethod

def
from_string(cls, s):

"""从字符串初始化一个账号"""

try
:
username, balance = s.split()
balance = decimal.
Decimal
(float(balance))

except

ValueError
:

raise

CreateAccountEr龙星妤ror
(
'input must follow pattern "{ACCOUNT_NAME} {BALANCE}"'
)

if
balance <
0
:

raise

CreateAccountError
(
'balance can not b鼻和膏e negative'
)

return
cls(username=username, balance=balance)
def
caculate_total_balance(accounts_data):

"""核算全部账号的总余额
"""
result =
0

for
account_string
in
accounts_data:

try
:
user =
Account
.from_string(account_string)

except

CreateAccountError
:

pass

else
:
result += user.balance

return
result
accounts_data = [

'piglei 96.5'
,

'cotton 21'
,

'invalid_data'
,

'roland $invalid_balance'
,

'alfred -3'
,
]
print
(caculate_total_balance(accounts_data))

在这个比方里,每逢咱们调用 Account诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技渠道.from_string 时,都必须运用 try/except 来捕获或许发作的反常。假定项目里需求调用许屡次该函数,这部分作业就变得十分繁琐了。针对这种状况,能够运用“空方针形式(Null object pattern)”来改进这个操控流。

Martin Fowler 在他的经典著作《重构》 顶用一个章节具体阐明过这个形式。简略来说,便是运用一个契合正常成果接口的“空类型”来代替空值回来/抛出反常,以此来下降调用方处理成果的本钱。

引进“空方针形式”后,上面的示例能够被修正成这样:

class

Account
:

# def __init__ 已省掉... ...

@classmethod

def
from_string(cls, s):

"""从字符串初始化一个账号
:returns: 假定输入合法,回来 Account obj一等废妾ect,不然回来 NullAccount
"""

try
:
username, balance = s.split()
balance = decimal.
Decimal
(float(balance))

except

ValueError
:

return

NullAccount
()

if
balance <
0
:

return

NullAccount
()

return
cls(username=username, balance=balance)
class

NullAccount
:
username =
''
balance =
0

@classmethod

def
from_string(cls, s):

raise

NotImplementedError

在新版代码里,我界说了 NullAccount 这个新类型,用来作为 from_string 失利时的过错成果回来。这样修正后的最大改变体现在 caculate_total_balance 部分:

def
caculate_total_balance(accounts_data):

"""核算全部账号的总余额
"""

return
sum(
Account
.from_string(s).bala变种食人鳄nce
for
s
in
accounts_data)

调整之后,调用方不用徐誉腾再显式运用 try 句子来处理过错,而是能够假定 Account.from_stri诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技渠道ng 函数总是会回来一个合法的 Account 方针,然后大大简化整个核算逻辑。

Hint:在 Python 国际里,“空方针形式”并不罕见,比方大名鼎鼎的 Django 结构里的 AnonymousUser 便是一个典型的 null object。

6. 运用生成器函数代替回来列表

在函数里回来列表特别常见,一般,咱们会先初始化一个列表 results=[],然后在循环体内运用 results.append(item) 函数填充它,最终在函数的结尾回来。

关于这类形式,咱们能够用生成器函数来简化它。粗犷点说,便是用 yielditem 代替 append 句子。运用生成器的函数一般更简练、也更具通用性。

def
foo_func(items):

for
item
in
items:

# ... 处理 item 后直接运用 yield 回来
诸葛亮简介,你是风儿我是沙,传奇缔造者-雷竞技官网_雷竞技官网下载_雷竞技渠道
yield
item

我在 系列第 4 篇文章“容器的门路” 里具体分析过这个形式,更多细节能够拜访文章,查找 “写扩展性更好的代码” 检查。

7. 约束递归的运用

当函数回来本身调用时,也便是 递归 发作时。递归是一种在特定场景下十分有用的编程技巧,但坏消息是:Python 言语对递归支撑的十分有限。

这份“有限的支撑”体现在许多方面。首要,Python 言语不支撑“尾递归优化”。别的 Python 对最大递归层级数也有着严厉的约束。

所以我主张:尽量少写递归。假定你想用递归处理问题,先想想它是不是能便利的用循环来代替。假定答案是必定的,那么就用循环来改写吧。假定无可奈何,必定需求运用递归时,请考虑下面几个点:

  • 函数输入数据规划是否安稳,是否必定不会超越 sys.getrecursionlimit() ox163规则的最大层数约束
  • 是否能够经过运用相似 阜宁焦爱芹视频functools.lru_cache 的缓存东西函数来下降递归层数

总结

在这篇文章中,我虚拟了一些与 Python 函数回来有关的场景,并针对每个场景供给了我的优化主张。最终再总结一下关键:

  • 让函数具有安稳的回来值,一个函数只做好一件事
  • 运用 functools.partial 界说方便函数
  • 抛出反常也是回来成果的一种方法,运用它来代替回来过错信息
  • 函数是否合适回来 None,由函数签名的“意义”所决议
  • 运用“空方针形式”能够简化调用方的过错处理逻辑
  • 多运用生成器函数,尽量用循环代替递归

看完文章的你,有没有什么想吐槽的?在谈论区给我留言

小编共享

一.400集python教程(第二季进步篇免费共享)

额定收拾的java300集视频教程!

400集python第二季进步篇丶300集java零基础教程+视频教程收取方法:

1、点赞+转发

2、重视《程序员Doctor》

3、私信小编(材料)即可免费收取

假定没有成功收取到教程地址,记得要发材料二字才能够获得!

十分感谢我们对小编的支撑和信赖,搭档也期望我们将此次材料进行共享,转载,让更多人需求的朋友看到,这样不只自己得到了协助,也能协助到其他人,是不是感觉很高兴呢!

热门
最新
推荐
标签