<Effective Python2>学习之第三章:函数

文章目录

文章目录

前言

N19:不要把函数返回的多个数值拆分到三个以上的变量中

1、详解

2、总结

N20:遇到意外情况时应该抛出异常,不要返回None

1、详解

2、总结

N22:用数量可变的位置参数,给函数设计清晰的参数列表

1、详解

2、总结


前言

提示:Effective Python第二版,作者是Brett Slatkin, Google首席软件工程师,立足于python3,主要讲解原理与常见用法。

第3章主要讲解函数在使用过程中的一些规范。


N19:不要把函数返回的多个数值拆分到三个以上的变量中

1、详解

1)unpacking 机制允许python返回一个以上的值,以元组的形式;

def my_func():
    return 1, 2

first, second = my_func()

2)  返回多个值时,可以利用(带星号的表达式)接受没有被普通变量捕获的值;

如:计算鳄鱼的长度与平均长度之比,并返回最长和最短的两条鳄鱼对应的比值。

def get_avg_ratio(numbers):
    average = sum(numbers)/len(numbers)
    scaled = [x/average for x in numbers]
    scaled.sort(reverse=True)
    return scaled

longest, *middle, shortest = get_avg_ratio(lengths)

3)返回值过多时,比如返回5个值,会造成两个问题:一是容易搞错顺序造成bug,二是调用函数并拆分返回值的那行代码会非常长,可能需要拆行。(最好不要这么写!!!)

如,返回鳄鱼长度的最小值、最大值,、平均值、中位值和样本总数量。

def get_stats(numbers):
    minimum = min(numbers)
    maximum = max(numbers)
    count = len(numbers)
    average = sum(numbers)/count

    sorted_numbers = sorted(number)
    middle = count // 2
    if count % 2 == 0:
        lower = sorted_number[middle - 1]
        upper = sorted_number[middle]
        midian = (lower + upper) / 2
    else:
        midian = sorted_number[middle]

    return minimum, maximum, average, median, count

minimum, maximum, average, median, count = get_stats(lengths)
 

2、总结

  • 函数可以把多个值合起来通过一个元组返回给调用者,可以利用unpacking 机制拆分;
  • 对于函数返回的多个值,将普通变量没有捕获的返回值全部捕获到一个(带星号的变量里);
  • 返回值拆分到四个或四个以上的变量很容易出错,最好不要!可以通过小类或namedtuple实例完成。

N20:遇到意外情况时应该抛出异常,不要返回None

1、详解

1)很多人喜欢返回None 来表示特殊情况,但存在一个问题:在条件表达式中,None与0、空字符串无法区分,容易出错。

比如:构造除法函数,除数为0会报错,返回None值;在后续对返回值进行使用的时候,按照情况1 的写法还不至于出错,但是情况2的写法,对于被除数为0,返回0的情况,也会输出Invalid inputs, 从而使得程序出现问题。针对该情况,有两种解决方案。

def carefule_divide(a,b):
    try:
        return a/b
    except ZeroDivisionError:
        return None

# 情况1
x,y = 1,0
result = carefule_divide(x,y)
if result is None:
    print('Invalid inputs')

# 情况2
x,y = 0,5
result = carefule_divide(x,y)  # shouldn't
if result is None:
    print('Invalid inputs')

2)方案一,构造二元组把计算结果分为两部分,第一个元素表示操作是否成功,第二个元素表示实际的计算值。使用时,调用者首先需要判断运算是否成功,再决定如何处理运算结果。(该方案的问题是,可能有一部分调用者容易忽略元组的第一个部分,从而无法有效区分。)

def carefule_divide(a,b):
    try:
        return True, a/b
    except ZeroDivisionError:
        return False, None

success, result = carefule_divide(x,y)
if not success:
    print('Invalid inputs')

# 偷懒的调用者
_, result = carefule_divide(x,y)
if not result:
    print('Invalid inputs')

3)方案二,不采用None表示特例,向调用方抛出异常,让其自行选择。

def carefule_divide(a,b):
    try:
        return a/b
    except ZeroDivisionError:
        raise ValueError('Invalid inputs')

# 调用方的工作
x,y = 5,2
try: 
    result = carefule_divide(x,y)
except ValueError:
    print('Invalid inputs')
else:
    print(f'Result:result')

2、总结

  • 用返回值None表示特殊情况,很容易出错。因为在条件表达式中,None跟空字符串、0无法区分,都相当于False;
  • 用异常表示特殊的情况,而不返回None,从而让调用者对异常情况进行处理。

N22:用数量可变的位置参数,给函数设计清晰的参数列表

1、详解

1)参数数量固定的函数,函数调用的时候可能会很乱;

比如values 没有值,也必须传一个空列表进去。

def log(message, values):
    if not values:
        print(message)
    else:
        values_str = '.'.join(str(x) for x in values)
        print(f'{message}:{values_str}')

#调用
log('My number are', [1,2])
log('Hi there', [])

#输出
My number are: 1, 2
Hi there

2)  让函数接受可变数量的位置参数,即varargs或star args(*args),能够使得函数调用更丝滑;

如:将log函数的第二个参数改为,*args,调用者只需要提供不带星号的参数即可。

def log(message, *values):
    if not values:
        print(message)
    else:
        values_str = '.'.join(str(x) for x in values)
        print(f'{message}:{values_str}')

#调用
log('My number are', [1,2])
log('Hi there')

#输出
My number are: 1, 2
Hi there

3)把已有序列中的元素当成参数,传给参数个数可变的函数,可以在传递序列时使用*操作符。

这种方式存在两个问题。一是程序先把这些参数转为一个元组,再把他们当成可选的位置参数传给参数,这中间需要使用yield函数,如果输入值过多,会造成程序崩溃;二是如果需要新增位置参数,那原来的调用操作需要全部更新,否则会报错或者导致结果异常。

def log(message, *values):
    if not values:
        print(message)
    else:
        values_str = '.'.join(str(x) for x in values)
        print(f'{message}:{values_str}')

#调用
favourites = [7,33,99]
log('Favorite colors', *favourites)

#输出
Favorite colors: 7, 33, 99

2、总结

  • def 定义函数时,*args可以让函数接收数量可变的位置参数;
  • 调用函数时,在序列前面加*,把其中的元素当成位置参数传给*args;
  • *操作符加在生成器前,传递参数时,程序可能会因为内存耗尽而崩溃;
  • 在接受*args的函数添加新的位置参数,会造成原来的调用方式需要改更,还有可能造成难以排查的bug。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Spring框架中的MultiValueMapConverter进行转换。具体实现代码如下: ```java import org.springframework.core.convert.converter.Converter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import java.util.Map; public class MapToMultiValueMapConverter implements Converter<Map<String, Object>, MultiValueMap<String, String>> { @Override public MultiValueMap<String, String> convert(Map<String, Object> source) { MultiValueMap<String, String> result = new LinkedMultiValueMap<>(); for (Map.Entry<String, Object> entry : source.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (value instanceof String) { result.add(key, (String) value); } else if (value instanceof String[]) { String[] values = (String[]) value; for (String v : values) { result.add(key, v); } } else if (value instanceof Iterable) { Iterable iterable = (Iterable) value; for (Object v : iterable) { result.add(key, String.valueOf(v)); } } else { result.add(key, String.valueOf(value)); } } return result; } } ``` 使用方式如下: ```java Map<String, Object> map = new HashMap<>(); map.put("name", "张三"); map.put("age", 18); map.put("hobbies", new String[]{"reading", "swimming"}); map.put("favorite_books", Arrays.asList("Java编程思想", "Effective Java")); MultiValueMap<String, String> multiValueMap = new MapToMultiValueMapConverter().convert(map); ``` 这样就可以将Map对象转换为MultiValueMap对象了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值