Python Data Science Toolbox part 1

写在前面

       这次更新拖得有点久,嗯当然,我觉得这次笔记还是挺充实的,有时间的小伙伴可以慢慢阅读,没时间的小伙伴点个收藏也是可以的~
       依照惯例,先列个提纲,今天的文章大致包括以下的内容:

  • Define functions without parameters
  • Define functions with one parameter
  • Define functions that return a value
  • Later: multiple arguments multiple return values

User-defined functions

       首先我们先来尝试建立一个给数字取平方的函数,来初步了解一下如何定义函数。

def square(): #<-Function header
    new_value=4**2 #<-Function body
    print(new_value)

       目前,我们的square函数的括号中没有带任何参数,我们将在后续的讲解中添加他。那么,无论何时我们调用此函数,都将运行函数的主体代码。此时,new_value的值被分配为16,然后将其打印出来。你可以通过输入预建函数square()来调用函数,但是,如果你想对除了4以外的数字求平方怎么办呢?你只需要在括号中添加一个参数即可:

def square(value): 
    new_value=value**2 
    print(new_value)
square(4)
Out[1]:16
square(5)
Out[1]:25

       现在我们可以将任何数字传递给函数,并且输出该数字的平方。这里需要注意一点的是:定义函数时,请在函数头中编写参数,调用函数时,需要将参数传递给函数。现在,函数square接受单个参数并打印出其平方值。
       OK,如果现在我们不想直接打印该值怎么办呢,我们要返回平方值,并将其分配给某给变量。您可以通过添加return关键字,然后加上要返回的值,使得函数返回新值:

#正确输入
def square(value):
    new_value=value**2
    return new_value
num=square(4)
print(num)
Out[1]:16
#错误输入
def squre(value):
    new_value=value**x
    print(new_value)
num=square(4)
print(num)
Out[1]:None

       如果不写return直接print,则只会执行打印命令,若再执行输出命令的话,则对应输出None,如上所述。

Docstrings

Docstrings are used to describe what your function does,such as the computations it performs or its return values.
       当添加这些描述到你的函数中,会方便任何阅读你函数的人。通过docstrings可以直接理解函数的功能,而不必跟踪函数定义中所有的代码。docstrings放置在function header之后,function body之前,并放在三个引号之间,比如对于我们的函数平方,合适的docstrings可以写成:Return the square of a value,如下所示:

def square(value):
    """Return the square of a value"""
    new_value=value**2
    return new_value
num=square(4)
print(num)

Multiple parameters and return values

       下面我们将刚刚所学习的平方函数升级一下,这次我们提出一个计算一个数的某个次幂,这里就涉及到了两个参数。

def raise_to_power(value1,value2):
    """Raise value1 to the power of value2"""
    new_value=value1**value2
    return new_value

       请注意,现在function header中有两个参数而不是一个,分别是value1和value2,并且参数传递的顺序与function header中参数的顺序是相对应的,其中value1是底数,value2是指数。

def raise_to_power(value1,value2):
    """Raise value1 to the power of value2"""
    new_value=value1**value2
    return new_value
result1=raise_to_power(2,3)
result2=raise_to_power(3,2)
print(result1)
print(result2)
Out[1]:8
Out[2]:9

       很显然可以看出区别,raise_to_power(2,3)是把2赋值给value1,也就是底数,把3赋值给value2,也就是指数,最后函数返回的就是2的3次方;raise_to_power(3,2)是把3赋值给value1,也就是底数,把2赋值给value2,也就是指数,最后函数返回的就是3的2次方。所以输出的result1值为8,而result2的值为9,所以在调用函数时请注意参数输入的顺序!

A quick jump into tuples

       你还可以使函数返回多个值,您可以通过在函数中构造元组来做到这一点。元组就像一个列表,其中可以包含多个值,但也有一定的区别:
Tuples:

  • Like a list - can contain multiple values
  • lmmutable - can’t modify values
  • constructed using parebtheses()
even_nums=(2,4,6)
print(type(even_nums))
Out[1]:<class 'tuple'>

Unpacking tuples&Accessing tuple elements

       在这里,我们构造一个包含3个元素的元组,您也可以在一行中将一个元组解压成为多个变量。

even_nums=(2,4,6)
a,b,c=even_nums
print(a)
print(b)
print(c)
Out[1]:2
Out[2]:4
Out[3]:6

       同样,你可以通过像使用列表一样访问单个元组元素

even_nums[1]
Out[1]:4

       下面是元组运用的例子:

def raise_both(value1,value2):
    """Raise value1 to the power of value2 and vice versa"""
    new_value1=value1**value2
    new_value2=value2**value1
    new_tuple=(new_value1,new_value2)
    return(new_tuple)
result=raise_both(2,3)
print(result)
Out[1]:(8,9)

Crash course on scope in functions

  • Not all objects are accessible everywhere in a script
  • Scope-part of the program where an object or name may be accessible

       现在,我们将在用户定义函数的上下文中讨论范围的概念。你已经在程序中定义了变量,到目前为止,您一直在使用这些变量而没有任何问题。但是,您应该知道的一件事是,并非所有你定义的变量可以在程序中的任意诶只访问。所以,你还需要知道三种类型的范围。

  • Global scope(全局范围):defined in the main body of a script.
  • Local scope (本地范围):defined inside a function
  • Built-in scope (内置作用域):names in the pre-defined built-ins module
           其中,全局范围意味着他是在脚本或在Python程序的主体中定义的;本地范围内意味着他是在函数内定义的,一旦执行完函数,本地范围内的任何名称都将停止存在,这意味着您不能在函数定义之外访问这些名称;而内置作用域,它是由Python提供的预定义的内置模块中的名称组成的,例如print和sum。

Global vs. local scope(1)

def square(value):
    """Return the square of a number"""
    new_val=value**2
    return new_val
square(3)
Out[1]:9
new_val
Out[1]:NameError: name 'new_val' is not defined

       如果我们在函数执行后尝试访问变量名称new_val,则会报错new_val未被定义,这里是因为他仅在函数本地范围内定义而不是在全局范围内定义的。

Global vs. local scope(2)

       那么如果我们在定义和调用函数之前,在全局定义了名称,有会出现怎么样的情况呢?

new_val=10
def square(value):
    """Return the square of a number"""
    new_val=value**2
    return new_val
square(3)
Out[1]:9
new_val
Out[1]:10

       简而言之,只要我们在全局范围内调用该名称,他就会访问全局名称,正如你在此处看到的一样,new_val=10。
       每当我们在函数的本地范围内调用该名称时,他将首先在本地范围内查找,这就是为什么square(3)的结果是9而不是10,如果Python在本底范围内找不到名称,则会在全局范围内查找。

Global vs. local scope(3)

       例如,在这里,我们在square函数中访问全局定义的new_val,请注意,访问的全局值是当时的值,而不是定义函数时的值,因此如下所示,如果我们重新分配new_val并调用函数square,我们会看到访问了new_val的新值。

new_val=10
def square(value):
    """Returns the square of a number."""
    new_value2=new_val**2
    return new_value2
square(3)
Out[1]:100
new_val=20
square(3)
Out[2]:400

       总的来说,当我们引用一个名称时,首先搜索本地范围,然后搜索全局范围,如果都不存在,再搜索内置作用域。

Gloval vs. local scope(4)

       现在,如果我们想在函数调用中更改全局名称的值,该怎么办?这就是关键字global派上用场的地方。我们现在来查看另一个案例,在函数定义中,我们通过使用关键字global跟随,希望访问和更改的全局变量的名称。

new_val=10
def square(value):
    """"Returns the square of a number."""
    global new_val
    new_val=new_val**2
    return new_val
square(3)
new_val=100

       当我们调用函数,函数按预期工作,结果显示100,现在我们继续new_val,我们看到通过运行函数square确实对全局值进行了平方。

Nested functions

       如果我们在一个函数中定义在了另一个函数内部,并且我们在内部函数引用了一个名称会怎样呢?

def outer(...):
    """..."""
    x=...
    def inner(...):
        """..."""
        y=x**2
    return ...

       Python会搜索内部函数的本地范围,如果找不到x,他将搜索外部函数的本地范围,之所以成为封闭函数,是因为它将内部函数包围了起来。如果Python在封闭函数的范围内找不到x,那么他就会搜索全局范围,然后再搜索内置作用域。
       但是我们为什么需要嵌套函数呢?假设我们要在一个函数中多次使用一个进程。例如,我们我要一个以3个数字为参数并在每个数字上执行相同功能的函数,一种方法是将计算过程写出3次:

def mod2plus5(x1,x2,x3):
    """Returns the remainder plus 5 of three values."""
    new_x1=x1%2+5
    new_x2=x2%2+5
    new_x3=x3%2+5
    return(new_x1,new_x2,new_x3)
print(mod2plus5(1,2,3))
Out[1]:(6, 5, 6)

       而使用嵌套函数则会显得更加简洁一些:

def mod2plus5(x1,x2,x3):
    """Returns the remainder plus 5 of three value."""
    def inner(x):
        """Returns the remainder plus 5 of a value."""
        return x%2+5
    return(inner(x1),inner(x2),inner(x3))
 Out[1]:(6,5,6)

       让我们再来看一下嵌套函数的另一个重要用例。

def raise_val(n):
   """Return the inner function"""
   def inner(x):
       """Raise x to the power of n"""
       raised=x**n
       return raised
   return inner(n)

       在此示例中,我们定义一个函数raise_vals,其中包含一个称为inner的内部函数,并且,他返回的是内部函数inner!raise_vals接受参数n并创建一个函数内部,该函数返回任意数字的n次幂。比如将数字2传递给raise_vals将创建一个对任何数字求平方的函数。同样,将数字3传递给raise_vals会创建一个对任何数字求立方的函数。

square=raise_val(2)
cube=raise_val(3)
print(square(2),cube(4))
Out[1]:4 64

       同样您可以使用关键字nonlocal在封闭范围内创建和更改名称。

def outer():
    """Prints the value of n."""
    n=1
    def inner():
        nonlocal n
        n=2
        print(n)
    inner()
    print(n)
outer()
Out[1]:2
       2

       总结一下,名称引用最多参考四个范围:即本地范围,然后是封闭函数(如果有),然后是全局范围,最后是内置作用域,这就是LEGB规则,其中L表示本地,E表示封闭,G表示全局,B表示内置。另外,请记住,分配名称只会创建或更改本地名称,除非它们是在全局或非本地语句中分配使用关键字global或关键字nonlocal声明。

Default and flexible argument

       假设您需要编写的函数需要多个参数,并且其中一些参数通常具有共同的值。在这种情况下,您希望能够在不显示指定,每个参数的情况下调用该函数,换句话说,您希望某些参数具有默认值。

Add a default argument

def power(number,pow=1):
    """Raise number to the power of pow"""
    new_value=number**pow
    return new_value
power(9,2)#此时底数是9,指数是2
Out[1]:81
power(9,1)#此时底数是9,指数是1
Out[2]:9
power(9)#此时没有给pow值,故指数默认是1
Out[3]:9

       假设您要编写一个函数,但不确定用户要传递多少个参数;例如一个接受浮点数或整数的函数,并将它们全部加起来,无论有多少,这就是输入灵活的参数。在此示例中,我们编写了对所有传递给它的参数求和的函数,

Flexible arguments:*args

def add_all(*args):
    """Sum all values in *args together."""
    #Initialize sum
    sum_all=0
    #Accumulate the sum
    for num in args:
        sum_all+=num
    return sum_all

       在函数定义中,我们使用参数*后跟args:然后将所有传递给函数的参数将调用到函数体中名为args的元组中,然后在函数主体中,编写我们所需的函数,我们将sum_all初始化为0,遍历元组args,并将其每个元素依次添加到sum_all,然后返回它。现在,我们可以使用任意数量的参数调用函数add_all将它们全部加起来!

add_all(1)
Out[1]:1
add_all(1,2)
Out[2]:3
add_all(5,10,15,20)
Out[3]:50

Flexible arguments:**kwargs

       你还可以使用双星号传递任意数量的关键字,我们将编写一个名为print_all的函数,该函数可以打印出标志符和传递给它们的参数。

def print_all(**kwargs):
    """Print out key-value pairs in **kwargs"""
    #print out the key-value pairs
    for key,value in kwargs.items():
        print(key+":"+value)
print_all(name="dumbledore",job="headmaster")
Out[1]:name:dumbledore
       job:headmaster

lambda functions

       这里介绍一种动态函数,这些函数称为lambda函数,因为您使用了关键字lambda

raise_to_power=lambda x,y:x**y
raise_to_power(2,3)
Out[1]:8

       在关键字lambda之后,我们指定参数的名称,然后我们使用冒号后跟表达式,该表达式制定我们希望函数返回的内容

Anonymous functions

       map函数,它带有两个参数,一个函数和一个序列(例如列表),并将函数应用于序列的所有元素.Function map: map(func,seq)

nums=[48,6,9,21,1]
square_all=map(lambda num:num**2,nums)
print(square_all)
Out[1]:<map object at 0x0000020863CEB400>
print(list(square_all))
Out[2]:[2304, 36, 81, 441, 1]

       打印square_all显示他实际上是一个地图对象,为了方便查看他的内容,我们可以将其转换为列表并将结果打印出来。从Out[2]中可以看出,已经打印出了列表中所有数字的平方。

Introduction to error handling

       当您错误地使用一个函数时,它会引发一个错误。比如float函数,对象只能是一个数字或字符串,并且字符串所包含的必须是一个数字。

float(2)
Out[1]:2.0
float("2.3")
Out[2]:2.3
float("abc")
Out[3]:ValueError: could not convert string to float: 'abc'

passing valid arguments

       现在我们来举个例子,比如开平方函数:

def sqrt(x):
    """Returns the square root of a number"""
    return x**(0.5)
sqrt(4)
Out[1]:2.0
sqrt(10)
Out[2]:3.1622776601683795
sqrt("hello")
Out[3]:TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'float'

       这种错误消息是呈现给使用函数的用户的,因为我们应更努力为所编写的函数提供有用的错误信息。

Errors and exceotions

       捕获此类异常的错误方法有两种方式:第一种是尝试使用try函数,如果所给参数能够运行函数并给出结果,那就正常输出,如果由于异常而无法运行,则运行错误代码。

       让我们重写平方根函数,但是这次我们尝试捕获异常。

def sqrt(x):
    """Returns the square root of a number"""
    try: return x**(0.5)
    except TypeError:print('x must be an int or float')
sqrt(10)
Out[1]:
sqrt("hello")
Out[2]:x must be an int or float

       通过观察,当我们捕获异常,同样的sqrt("hello")结果显示出来就不一样了。那第二种方法是什么呢?通常不仅仅是打印错误消息,我们实际上想通过使用关键字raise引发错误,例如当平方根函数应用于负数时,可能会执行我们我们不希望的操作,实际上会返回一个我们可能不需要的复数,假设我们不希望函数对复数起作用。

sqrt(-9)#重新定义之前
Out[1]:(1.8369701987210297e-16+3j)
def sqrt(x):
    """Returns the squre root of a number"""
    if x<0:
       raise ValueError('x must be non-negative')
    try:return x**0.5
    except TyprError:print('x must be an int or a float')
sqrt(-9)#重新定义之后
Out[2]:ValueError: x must be non-negative

       嗯哼,这就是引发错误。经过这部分的学习,应该可以创建大部分您所需要的函数了。感谢阅读,90度鞠躬。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值