zoukankan      html  css  js  c++  java
  • python基础-3

    python的函数参数传递:

    我想,这个标题或许是很多初学者的问题。尤其是像我这样的对C/C++比较熟悉,刚刚进入python殿堂的朋友们

    。C/C++的函数参数的传递方式根深蒂固的影响这我们的思维--引用?传值?究竟是那种呢。

    呵呵,语言的特性决定了是使用的方法,那么,现在我们来探究一下python的函数参数传递方式。

    在开始之前,我们有必要分清一下python的一些基础概念。

    首先要说的是:变量 与 对象

    在python中,类型属于对象,变量是没有类型的,这正是python的语言特性,也是吸引着很多pythoner的一点。所有的变量都可以理解 是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。所以,希望大家在看到一个python变量的时候,把变量和真正的内存对象分开。

    类型是属于对象的,而不是变量。这样,很多问题就容易思考了。

    例如: 

    nfoo = 1   #一个指向int数据类型的nfoo(再次提醒,nfoo没有类型)

    lstFoo = [1]   #一个指向list类型的lstFoo,这个list中包含一个整数1。

    对应于上一个概念,就必须引出另了另一概念,这就是“可更改”(mutable)与“不可更改”(immutable)对象

    对于python比较熟悉的人们都应该了解这个事实,在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。那么,这些所谓的可改变和不可改变影响着什么呢?

    还是上面的例子:

    nfoo = 2        

    这时,内存中原始的1对象因为不能改变,于是被“抛弃”,另nfoo指向一个新的int对象,其值为2

    lstFoo[0] = 2  

    更改list中第一个元素的值,因为list是可改变的,所以,第一个元素变更为2,其实应该说有一个新int对象被指定给lstFoo 所指向的对象的第一个值,但是对于lstFoo 来说,所指向的对象,并没有变化,就是这个看似void*的变量所指向的对象仍旧是刚刚的那个有一个int对象的list。(听着有点晕吧,仔细琢磨一下 就明白了,嘿)

    好了,被我这么填鸭似的复习了一下python的基础知识,改转回题目的问题了,Python的函数参数传递:传值?引用?

    对于变量(与对象相对的概念),其实,python函数参数传递可以理解为就是变量传值操作(注意哦,我说的是变量,不是对象  =_= )

    接着说例子好了:

    def ChangeInt( a ):

          a = 10  # change the number

    nfoo = 2 

    ChangeInt(nfoo)

    print nfoo #结果是2

    这时发生了什么,有一个int对象2,和指向它的变量nfoo,当传递给ChangeInt的时候,按照传值的方式,复制了变量nfoo的值,这样,a就是nfoo指向同一个Int对象了,函数中a=10的时候,发生什么?

    (还记得我上面讲到的那些概念么),int是不能更改的对象,于是,做了一个新的int对象,另a指向它(但是此时,被变量nfoo指向的对象,没有发生变化),于是在外面的感觉就是函数没有改变nfoo的值,看起来像C++中的传值方式。

    def ChangeList( a ):

          a[0] = 10  # change the number

    lstFoo = [2]

    ChangeList(lstFoo )

    print nfoo #结果是[10]

    当传递给ChangeList的时候,变量仍旧按照“传值”的方式,复制了变量lstFoo 的值,于是a和lstFoo 指向同一个对象,但是,list是可以改变的对象,对a[0]的操作,就是对lstFoo指向的对象的内容的操作,于是,这时的a[0] = 10,就是更改了lstFoo 指向的对象的第一个元素,所以,再次输出lstFoo 时,显示[10],内容被改变了,看起来,像C++中的按引用传递。

     总结:函数的形参和实参指向相同的对象,若该对象是可更改的(列表或字典),则在函数内部对形参的改变使得对象发生改变,则实参必然发生改变。否则对形参进行改变只是让形参指向新的对象,不会影响实参

    python函数参数的定义:

    在python中函数参数的定义主要有四种方式:
    1.F(arg1,arg2,...)
    这 是最常见的定义方式,一个函数可以定义任意个参数,每个参数间用逗号分割,用这种方式定义的函数在调用的的时候也必须在函数名后的小括号里提供个数相等的 值(实际参数),而且顺序必须相同,也就是说在这种调用方式中,形参和实参的个数必须一致,而且必须一一对应,也就是说第一个形参对应这第一个实参。例 如:
    def a(x,y):
    print x,y
    调用该函数,a(1,2)则x取1,y取2,形参与实参相对应,如果a(1)或者a(1,2,3)则会报错。

    2.F(arg1,arg2=value2,...)
    这种方式就是第一种的改进版,提供了默认值
    def a(x,y=3):
    print x,y
    调用该函数,a(1,2)同样还是x取1,y取2,但是如果a(1),则不会报错了,这个时候x还是1,y则为默认的3。上面这俩种方式,还可以更换参数位置,比如a(y=8,x=3)用这种形式也是可以的。

    3.F(*arg1)
    上 面俩个方式是有多少个形参,就传进去多少个实参,但有时候会不确定有多少个参数,则此时第三种方式就比较有用,它以一个*加上形参名的方式来表示这个函数 的实参个数不定,可能为0个也可能为n个。注意一点是,不管有多少个,在函数内部都被存放在以形参名为标识符的tuple中。
    >>> def a(*x):
    if len(x)==0:
    print 'None'
    else:
    print x
    >>> a(1)
    (1,)        #存放在元组中
    >>> a()
    None
    >>> a(1,2,3)
    (1, 2, 3)
    >>> a(m=1,y=2,z=3)

    Traceback (most recent call last):
    File "<pyshell#16>", line 1, in -toplevel-
    a(m=1,y=2,z=3)
    TypeError: a() got an unexpected keyword argument 'm'

    4.F(**arg1)
    形参名前加俩个*表示,参数在函数内部将被存放在以形式名为标识符的dictionary中,这时调用函数的方法则需要采用arg1=value1,arg2=value2这样的形式。
    >>> def a(**x):
    if len(x)==0:
    print 'None'
    else:
    print x  
    >>> a()
    None
    >>> a(x=1,y=2)
    {'y': 2, 'x': 1}      #存放在字典中
    >>> a(1,2)            #这种调用则报错

    Traceback (most recent call last):
    File "<pyshell#25>", line 1, in -toplevel-
    a(1,2)
    TypeError: a() takes exactly 0 arguments (2 given)

    上面介绍了四种定义方式,接下来看函数参数在调用过程中是怎么被解析的,其实只要记住上面这四种方法优先级依次降低,先1,后2,再3,最后4,也就是先把方式1中的arg解析,然后解析方式2中的arg=value,再解析方式3,即是把多出来的arg这种形式的实参组成个tuple传进去,最后把剩下的key=value这种形式的实参组成一个dictionary传给带俩个星号的形参,也就方式4。

    >>> def test(x,y=1,*a,**b):
        print x,y,a,b
    >>> test(1)
    1 1 () {}
    >>> test(1,2)
    1 2 () {}
    >>> test(1,2,3)
    1 2 (3,) {}
    >>> test(1,2,3,4)
    1 2 (3, 4) {}
    >>> test(x=1,y=2)
    1 2 () {}
    >>> test(1,a=2)
    1 1 () {'a': 2}
    >>> test(1,2,3,a=4)
    1 2 (3,) {'a': 4}
    >>> test(1,2,3,y=4)

    Traceback (most recent call last):
    File "<pyshell#52>", line 1, in -toplevel-
    test(1,2,3,y=4)
    TypeError: test() got multiple values for keyword argument 'y'

    匿名函数:

    lambda关键词能创建小型匿名函数。这种函数得名于省略了用def声明函数的标准步骤。

    • Lambda函数能接收任何数量的参数但只能返回一个表达式的值,同时不能包含命令或多个表达式。

    • 匿名函数不能直接调用print,因为lambda需要一个表达式。

    • lambda函数拥有自己的名字空间,且不能访问自有参数列表之外或全局名字空间里的参数。

    #可写函数说明
    sum = lambda arg1, arg2: arg1 + arg2;
    #调用sum函数
    print "Value of total : ", sum( 10, 20 )
    print "Value of total : ", sum( 20, 20 )

    python模块:

    一个Python表达式可以访问局部命名空间和全局命名空间里的变量。如果一个局部变量和一个全局变量重名,则局部变量会覆盖全局变量。

    Python会智能地猜测一个变量是局部的还是全局的,它假设任何在函数内赋值的变量都是局部的

    因此,如果要给全局变量在一个函数里赋值,必须使用global语句

    Money = 2000
    def AddMoney():
       # 想改正代码就取消以下注释,不然会出错,因为没有在访问前声明一个局部变量
       # global Money
       Money = Money + 1
     
    print Money
    AddMoney()
    print Money

    如果在函数内部调用locals(),返回的是所有能在该函数里访问的命名。

    如果在函数内部调用globals(),返回的是所有在该函数里能访问的全局名字。

    两个函数的返回类型都是字典。所以名字们能用keys()函数摘取。

    range函数用法:

    >>> range(1,5) #代表从15(不包含5)

    [1, 2, 3, 4]

    >>> range(1,5,2) #代表从15,间隔2(不包含5)

    [1, 3]

    >>> range(5) #代表从05(不包含5)

    [0, 1, 2, 3, 4]

    raw_input([prompt]) 函数从标准输入读取一个行,并返回一个字符串(去掉结尾的换行符):

    str = raw_input("Enter your input: ");
    print "Received input is : ", str

    input([prompt]) 函数和raw_input([prompt]) 函数基本可以互换,但是input会假设你的输入是一个有效的Python表达式,并返回运算结果。

    文件操作:

    r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
    rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
    r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
    rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
    w 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
    wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
    w+ 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
    wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
    a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
    a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
    ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

    open函数:

    file object = open(file_name [, access_mode][, buffering])

    buffering:如果buffering的值被设为0,就不会有寄存。如果buffering的值取1,访问文件时会寄存行。如果将buffering的值设为大于1的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。

    File对象的属性:

    file.closed

    返回true如果文件已被关闭,否则返回false

    file.mode

    返回被打开文件的访问模式。

    file.name

    返回文件的名称。

    file.softspace

    如果用print输出后,必须跟一个空格符,则返回false。否则返回true

    close方法:

    File对象的close()方法刷新缓冲区里任何还没写入的信息,并关闭该文件,这之后便不能再进行写入。

    write方法:

    fileObject.write(string);

    read方法:

    语法:

    fileObject.read([count]);

    在这里,被传递的参数是要从已打开文件中读取的字节计数。该方法从文件的开头开始读入,如果没有传入count,它会尝试尽可能多地读取更多的内容,很可能是直到文件的末尾。

    Tell()方法告诉你文件内的当前位置;换句话说,下一次的读写会发生在文件开头这么多字节之后

    seekoffset [,from])方法改变当前文件的位置Offset变量表示要移动的字节数。From变量指定开始移动字节的参考位置。

    如果from被设为0,这意味着将文件的开头作为移动字节的参考位置。如果设为1,则使用当前的位置作为参考位置。如果它被设为2,那么该文件的末尾将作为参考位置。

    # 把指针再次重新定位到文件开头
    position = fo.seek(0, 0);

    Pythonos模块提供了帮你执行文件处理操作的方法,比如重命名和删除文件。

    要使用这个模块,你必须先导入它,然后可以调用相关的各种功能。

    rename()方法:

    rename()方法需要两个参数,当前的文件名和新文件名。

    语法:

    os.rename(current_file_name, new_file_name)
    remove方法:

    你可以用remove()方法删除文件,需要提供要删除的文件名作为参数。

    语法: os.remove(file_name)

    mkdir方法:

    可以使用os模块的mkdir()方法在当前目录下创建新的目录们。你需要提供一个包含了要创建的目录名称的参数。

    语法:

    os.mkdir("newdir")

    chdir方法:

    可以用chdir()方法来改变当前的目录。chdir()方法需要的一个参数是你想设成当前目录的目录名称。

    语法:

    os.chdir("newdir")

    getcwd方法:

    getcwd()方法显示当前的工作目录。

    语法:

    os.getcwd()

    rmdir()方法:

    删除目录,目录名称以参数传递。

    在删除这个目录之前,它的所有内容应该先被清除。

    语法:

    os.rmdir('dirname')

    异常处理:

    捕捉异常可以使用try/except语句。

    try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。

    如果你不想在异常发生时结束你的程序,只需在try里捕获它。

    语法:

    以下为简单的try....except...else的语法:

    try:
    <语句>        #运行别的代码
    except <名字>:
    <语句>        #如果在try部份引发了'name'异常
    except <名字>,<数据>:
    <语句>        #如果引发了'name'异常,获得附加的数据
    else:
    <语句>        #如果没有异常发生

    try的工作原理是,当开始一个try语句后,python就在当前程序的上下文中作标记,这样当异常出现时就可以回到这里,try子句先执行,接下来会发生什么依赖于执行时是否出现异常。

    • 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。

    • 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印缺省的出错信息)。

    • 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。

    使用except而不带任何异常类型:

    try:
       You do your operations here;
       ......................
    except:
       If there is any exception, then execute this block.
       ......................
    else:
       If there is no exception then execute this block. 

    以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常

    也可以使用一个except语句来处理多个异常信息

    try:
       You do your operations here;
       ......................
    except(Exception1[, Exception2[,...ExceptionN]]]):
       If there is any exception from the given exception list, 
       then execute this block.
       ......................
    else:
       If there is no exception then execute this block.  

    try-finally 语句无论是否发生异常都将执行最后的代码。

    try:
    <语句>
    finally:
    <语句>    #退出try时总会执行

    注意:你可以使用except语句或者finally语句,但是两者不能同时使用。else语句也不能与finally语句同时使用

    一个异常可以带上参数,可作为输出的异常信息参数。

    你可以通过except语句来捕获异常的参数,如下所示:

    def temp_convert(var):
       try:
          return int(var)
       except ValueError, Argument:
          print "The argument does not contain numbers
    ", Argument
    
    # Call above function here.
    temp_convert("xyz");

    以上程序执行结果如下:

    The argument does not contain numbers
    invalid literal for int() with base 10: 'xyz'

    注意:上述Argument可以是任意名称



  • 相关阅读:
    纯CSS3实现3D动画导航,html5 webRTC技术实现免费网页电话拨打
    Base64编解码Android和ios的例子,补充JNI中的例子
    新春寄语
    彩票号码OC呈现
    iOS CFNetwork报错
    Android常用库
    高性能服务端访问设计
    Tomcat的ISO-8859-1
    迅达云s3cmd客户端mac平台部署说明
    Android.os.NetworkOnMainThreadException
  • 原文地址:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/4216639.html
Copyright © 2011-2022 走看看