• python基础4


    函数介绍

    <1>什么是函数

    请看如下代码:

        print "                            _ooOoo_  "
        print "                           o8888888o  "
        print "                           88  .  88  "
        print "                           (| -_- |)  "
        print "                            O\ = /O  "
        print "                        ____/`---'\____  "
        print "                      .   ' \| |// `.  "
        print "                       / \||| : |||// \  "
        print "                     / _||||| -:- |||||- \  "
        print "                       | | \\\ - /// | |  "
        print "                     | \_| ''\---/'' | |  "
        print "                      \ .-\__ `-` ___/-. /  "
        print "                   ___`. .' /--.--\ `. . __  "
        print "                ."" '< `.___\_<|>_/___.' >'"".  "
        print "               | | : `- \`.;`\ _ /`;.`/ - ` : | |  "
        print "                 \ \ `-. \_ __\ /__ _/ .-` / /  "
        print "         ======`-.____`-.___\_____/___.-`____.-'======  "
        print "                            `=---='  "
        print "  "
        print "         .............................................  "
        print "                  佛祖镇楼                  BUG辟易  "
        print "          佛曰:  "
        print "                  写字楼里写字间,写字间里程序员;  "
        print "                  程序人员写程序,又拿程序换酒钱。  "
        print "                  酒醒只在网上坐,酒醉还来网下眠;  "
        print "                  酒醉酒醒日复日,网上网下年复年。  "
        print "                  但愿老死电脑间,不愿鞠躬老板前;  "
        print "                  奔驰宝马贵者趣,公交自行程序员。  "
        print "                  别人笑我忒疯癫,我笑自己命太贱;  "
        print "                  不见满街漂亮妹,哪个归得程序员?"

    运行后的现象:

    函数定义和调用

    <1>定义函数

    定义函数的格式如下:

     
        def 函数名():
            代码

    demo:

     
        # 定义一个函数,能够完成打印信息的功能
        def printInfo():
            print '------------------------------------'
            print '         人生苦短,我用Python'
            print '------------------------------------'

    <2>调用函数

    定义了函数之后,就相当于有了一个具有某些功能的代码,想要让这些代码能够执行,需要调用它

    调用函数很简单的,通过 函数名() 即可完成调用

    demo:

     
        # 定义完函数后,函数是不会自动执行的,需要调用它才可以
        printInfo()

    <3>练一练

    要求:定义一个函数,能够输出自己的姓名和年龄,并且调用这个函数让它执行

    • 使用def定义函数
    • 编写完函数之后,通过 函数名() 进行调用

    函数的文档说明

    >>> def test(a,b):
    ...     "用来完成对2个数求和"
    ...     print("%d"%(a+b))
    ... 
    >>> 
    >>> test(11,22)
    33

    如果执行,以下代码

    >>> help(test)

    能够看到test函数的相关说明

    Help on function test in module __main__:
     
    test(a, b)
        用来完成对2个数求和
    (END)

    函数参数(一)

    思考一个问题,如下:

    现在需要定义一个函数,这个函数能够完成2个数的加法运算,并且把结果打印出来,该怎样设计?下面的代码可以吗?有什么缺陷吗?

       def add2num():
           a = 11
           b = 22
           c = a+b
           print c

    为了让一个函数更通用,即想让它计算哪两个数的和,就让它计算哪两个数的和,在定义函数的时候可以让函数接收数据,就解决了这个问题,这就是函数的参数

    <1>定义带有参数的函数

    示例如下:

        def add2num(a, b):
            c = a+b
            print c

    <2>调用带有参数的函数

    以调用上面的add2num(a, b)函数为例:

     
        def add2num(a, b):
            c = a+b
            print c
     
        add2num(11, 22) #调用带有参数的函数时,需要在小括号中,传递数据

    调用带有参数函数的运行过程:

    <3>练一练

    要求:定义一个函数,完成前2个数完成加法运算,然后对第3个数,进行减法;然后调用这个函数

    • 使用def定义函数,要注意有3个参数
    • 调用的时候,这个函数定义时有几个参数,那么就需要传递几个参数
    • 定义时小括号中的参数,用来接收参数用的,称为“形参”
    • 调用时小括号中的参数,用来传递给函数用的,称为“实参”

    <4>调用函数时参数的顺序

    >>> def test(a,b):
    ...     print(a,b)
    ... 
    >>> test(1,2)
    1 2
    >>> test(b=1,a=2)
    2 1
    >>> 
    >>> test(b=1,2)
      File "<stdin>", line 1
    SyntaxError: positional argument follows keyword argument
    >>> 
    >>> 

    <4>小总结

    函数返回值(一)

    <1>“返回值”介绍

    现实生活中的场景:

    我给儿子10块钱,让他给我买包烟。这个例子中,10块钱是我给儿子的,就相当于调用函数时传递到参数,让儿子买烟这个事情最终的目标是,让他把烟给你带回来然后给你对么,,,此时烟就是返回值

    开发中的场景:

    定义了一个函数,完成了获取室内温度,想一想是不是应该把这个结果给调用者,只有调用者拥有了这个返回值,才能够根据当前的温度做适当的调整

    综上所述:

    • 所谓“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果

    <2>带有返回值的函数

    想要在函数中把结果返回给调用者,需要在函数中使用return

    如下示例:

        def add2num(a, b):
            c = a+b
            return c

    或者

        def add2num(a, b):
            return a+b

    <3>保存函数的返回值

    在本小节刚开始的时候,说过的“买烟”的例子中,最后儿子给你烟时,你一定是从儿子手中接过来对么,程序也是如此,如果一个函数返回了一个数据,那么想要用这个数据,那么就需要保存

    保存函数的返回值示例如下:

        #定义函数
        def add2num(a, b):
            return a+b
     
        #调用函数,顺便保存函数的返回值
        result = add2num(100,98)
     
        #因为result已经保存了add2num的返回值,所以接下来就可以使用了
        print result

    结果:

        198

    4种函数的类型

    函数根据有没有参数,有没有返回值,可以相互组合,一共有4种

    • 无参数,无返回值
    • 无参数,又反悔
    • 有参数,无返回值
    • 有参数,有返回值

    <1>无参数,无返回值的函数

    此类函数,不能接收参数,也没有返回值,一般情况下,打印提示灯类似的功能,使用这类的函数

        def printMenu():
            print('--------------------------')
            print('      xx涮涮锅 点菜系统')
            print('')
            print('  1.  羊肉涮涮锅')
            print('  2.  牛肉涮涮锅')
            print('  3.  猪肉涮涮锅')
            print('--------------------------')

    结果:

    <2>无参数,有返回值的函数

    此类函数,不能接收参数,但是可以返回某个数据,一般情况下,像采集数据,用此类函数

     
        # 获取温度
        def getTemperature():
     
            #这里是获取温度的一些处理过程
     
            #为了简单起见,先模拟返回一个数据
            return 24
     
        temperature = getTemperature()
        print('当前的温度为:%d'%temperature)

    结果:

        当前的温度为: 24

    <3>有参数,无返回值的函数

    此类函数,能接收参数,但不可以返回数据,一般情况下,对某些变量设置数据而不需结果时,用此类函数

    <4>有参数,有返回值的函数

    此类函数,不仅能接收参数,还可以返回某个数据,一般情况下,像数据处理并需要结果的应用,用此类函数

     
        # 计算1~num的累积和
        def calculateNum(num):
     
            result = 0
            i = 1
            while i<=num:
     
                result = result + i
     
                i+=1
     
            return result
     
        result = calculateNum(100)
        print('1~100的累积和为:%d'%result)

    结果:

        1~100的累积和为: 5050

    <5>小总结

    • 函数根据有没有参数,有没有返回值可以相互组合
    • 定义函数时,是根据实际的功能需求来设计的,所以不同开发人员编写的函数类型各不相同

    函数的嵌套调用

     
        def testB():
            print('---- testB start----')
            print('这里是testB函数执行的代码...(省略)...')
            print('---- testB end----')
     
     
        def testA():
     
            print('---- testA start----')
     
            testB()
     
            print('---- testA end----')
     
        testA()

    结果:

        ---- testA start----
        ---- testB start----
        这里是testB函数执行的代码...(省略)...
        ---- testB end----
        ---- testA end----

    小总结:

    • 一个函数里面又调用了另外一个函数,这就是所谓的函数嵌套调用 
    • 如果函数A中,调用了另外一个函数B,那么先把函数B中的任务都执行完毕之后才会回到上次函数A执行的位置
    • 感受函数的嵌套调用
    • 感受程序设计的思路,复杂问题分解为简单问题

    函数应用:打印图形和数学计算

    目标

    思考&实现1

    1. 写一个函数打印一条横线
    2. 打印自定义行数的横线
    3. 写一个函数求三个数的和
    4. 写一个函数求三个数的平均值

    参考代码1

     
    # 打印一条横线
    def printOneLine():
        print("-"*30)
     
    # 打印多条横线
    def printNumLine(num):
        i=0
     
        # 因为printOneLine函数已经完成了打印横线的功能,
        # 只需要多次调用此函数即可
        while i<num:
            printOneLine()
            i+=1
     
    printNumLine(3)

    思考&实现2

    参考代码2

     
    # 求3个数的和
    def sum3Number(a,b,c):
        return a+b+c # return 的后面可以是数值,也可是一个表达式
     
    # 完成对3个数求平均值
    def average3Number(a,b,c):
     
        # 因为sum3Number函数已经完成了3个数的就和,所以只需调用即可
        # 即把接收到的3个数,当做实参传递即可
        sumResult = sum3Number(a,b,c)
        aveResult = sumResult/3.0
        return aveResult
     
    # 调用函数,完成对3个数求平均值
    result = average3Number(11,2,55)
    print("average is %d"%result)

    全局变量

    <1>什么是全局变量

    如果一个变量,既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是全局变量

    demo如下:

     
        # 定义全局变量
        a = 100
     
        def test1():
            print(a)
     
        def test2():
            print(a)
     
        # 调用函数
        test1()
        test2()

    运行结果:

    <2>全局变量和局部变量名字相同问题

    看如下代码:

    <3>修改全局变量

    既然全局变量,就是能够在所以的函数中进行使用,那么可否进行修改呢?

    代码如下

    <4>总结1:

    • 在函数外边定义的变量叫做全局变量
    • 全局变量能够在所有的函数中进行访问
    • 如果在函数中修改全局变量,那么就需要使用global进行声明,否则出错
    • 如果全局变量的名字和局部变量的名字相同,那么使用的是局部变量的,小技巧强龙不压地头蛇
    • 在函数中不使用global声明全局变量时不能修改全局变量的本质是不能修改全局变量的指向,即不能将全局变量指向新的数据。
    • 对于不可变类型的全局变量来说,因其指向的数据不能修改,所以不使用global时无法修改全局变量。
    • 对于可变类型的全局变量来说,因其指向的数据可以修改,所以不使用global时也可修改全局变量。

    <5>可变类型的全局变量

    >>> a = 1
    >>> def f():
    ...     a += 1
    ...     print a
    ...
    >>> f()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in f
    UnboundLocalError: local variable 'a' referenced before assignment
    >>> 
    >>> 
    >>> li = [1,]
    >>> def f2():
    ...     li.append(1)
    ...     print li
    ...
    >>> f2()
    [1, 1]
    >>> li
    [1, 1]

    <5>总结2:

    函数返回值(二)

    在python中我们可不可以返回多个值?

    >>> def divid(a, b):
    ...     shang = a//b
    ...     yushu = a%b 
    ...     return shang, yushu
    ...
    >>> sh, yu = divid(5, 2)
    >>> sh
    5
    >>> yu
    1

    本质是利用了元组

    函数参数(二)

    1.缺省参数

    调用函数时,缺省参数的值如果没有传入,则被认为是默认值。下例会打印默认的age,如果age没有被传入:

    def printinfo( name, age = 35 ):
       # 打印任何传入的字符串
       print "Name: ", name
       print "Age ", age
     
    # 调用printinfo函数
    printinfo(name="miki" )
    printinfo( age=9,name="miki" )

    以上实例输出结果:

    Name:  miki
    Age  35
    Name:  miki
    Age  9

    注意:带有默认值的参数一定要位于参数列表的最后面。

    >>> def printinfo(name, age=35, sex):
    ...     print name
    ...
      File "<stdin>", line 1
    SyntaxError: non-default argument follows default argument

    2.不定长参数

    有时可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,声明时不会命名。

    基本语法如下:

        def functionname([formal_args,] *args, **kwargs):
           "函数_文档字符串"
           function_suite
           return [expression]

    加了星号(*)的变量args会存放所有未命名的变量参数,args为元组;而加**的变量kwargs会存放命名参数,即形如key=value的参数,kwargs为字典。

    >>> def fun(a, b, *args, **kwargs):
    ...     """可变参数演示示例"""
    ...     print "a =", a
    ...     print "b =", b
    ...     print "args =", args
    ...     print "kwargs: "
    ...     for key, value in kwargs.items():
    ...         print key, "=", value
    ...
    >>> fun(1, 2, 3, 4, 5, m=6, n=7, p=8# 注意传递的参数对应
    a = 1
    b = 2
    args = (3, 4, 5)
    kwargs: 
    p = 8
    m = 6
    n = 7
    >>> 
    >>> 
    >>> 
    >>> c = (3, 4, 5)
    >>> d = {"m":6, "n":7, "p":8}
    >>> fun(1, 2, *c, **d)    # 注意元组与字典的传参方式
    a = 1
    b = 2
    args = (3, 4, 5)
    kwargs: 
    p = 8
    m = 6
    n = 7
    >>> 
    >>> 
    >>> 
    >>> fun(1, 2, c, d) # 注意不加星号与上面的区别
    a = 1
    b = 2
    args = ((3, 4, 5), {'p': 8, 'm': 6, 'n': 7})
    kwargs:
    >>> 
    >>> 

    3.引用传参

    • 可变类型与不可变类型的变量分别作为函数参数时,会有什么不同吗?
    • Python有没有类似C语言中的指针传参呢?
    >>> def selfAdd(a):
    ...     """自增"""
    ...     a += a
    ...
    >>> a_int = 1
    >>> a_int
    1
    >>> selfAdd(a_int)
    >>> a_int
    1
    >>> a_list = [1, 2]
    >>> a_list
    [1, 2]
    >>> selfAdd(a_list)
    >>> a_list
    [1, 2, 1, 2]

    Python中函数参数是引用传递(注意不是值传递)。对于不可变类型,因变量不能修改,所以运算不会影响到变量自身;而对于可变类型来说,函数体中的运算有可能会更改传入的参数变量。

    想一想为什么

    >>> def selfAdd(a):
    ...     """自增"""
    ...     a = a + a   # 我们更改了函数体的这句话
    ...
    >>> a_int = 1
    >>> a_int
    1
    >>> selfAdd(a_int)
    >>> a_int
    1
    >>> a_list = [1, 2]
    >>> a_list
    [1, 2]
    >>> selfAdd(a_list)
    >>> a_list
    [1, 2]      # 想一想为什么没有变呢?

    递归函数

    <1>什么是递归函数

    通过前面的学习知道一个函数可以调用其他函数。

    如果一个函数在内部不调用其它的函数,而是自己本身的话,这个函数就是递归函数。

    <2>递归函数的作用

    举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n

    解决办法1:

    匿名函数

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

    lambda函数的语法只包含一个语句,如下:

        lambda [arg1 [,arg2,.....argn]]:expression

    如下实例:

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

    以上实例输出结果:

        Value of total :  30
        Value of total :  40

    Lambda函数能接收任何数量的参数但只能返回一个表达式的值

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

    应用场合

    函数作为参数传递

    1. 自己定义函数
    2. 作为内置函数的参数
    2.  >>> def fun(a, b, opt):
    3.  ...     print "a =", a
    4.  ...     print "b =", b
    5.  ...     print "result =", opt(a, b)
    6.  ...
    7.  >>> fun(1, 2, lambda x,y:x+y)
    8.  a = 1
    9.  b = 2
    10. result = 3

    想一想,下面的数据如何指定按age或name排序?

    stus = [
        {"name":"zhangsan", "age":18}, 
        {"name":"lisi", "age":19}, 
        {"name":"wangwu", "age":17}
    ]

    按name排序:

    >>> stus.sort(key = lambda x:x['name'])
    >>> stus
    [{'age': 19, 'name': 'lisi'}, {'age': 17, 'name': 'wangwu'}, {'age': 18, 'name': 'zhangsan'}]

    按age排序:

    >>> stus.sort(key = lambda x:x['age'])
    >>> stus
    [{'age': 17, 'name': 'wangwu'}, {'age': 18, 'name': 'zhangsan'}, {'age': 19, 'name': 'lisi'}]

    函数使用注意事项

    1.自定义函数

    <1>无参数、无返回值

        def 函数名():
            语句

    <2>无参数、有返回值

        def 函数名():
            语句
            return 需要返回的数值

    注意:

    • 一个函数到底有没有返回值,就看有没有return,因为只有return才可以返回数据
    • 在开发中往往根据需求来设计函数需不需要返回值
    • 函数中,可以有多个return语句,但是只要执行到一个return语句,那么就意味着这个函数的调用完成

    <3>有参数、无返回值

        def 函数名(形参列表):
            语句

    注意:

    • 在调用函数时,如果需要把一些数据一起传递过去,被调用函数就需要用参数来接收
    • 参数列表中变量的个数根据实际传递的数据的多少来确定

    <4>有参数、有返回值

        def 函数名(形参列表):
            语句
            return 需要返回的数值

    <5>函数名不能重复

    2.调用函数

    <1>调用的方式为:

        函数名([实参列表])

    <2>调用时,到底写不写实参

    • 如果调用的函数在定义时有形参,那么在调用的时候就应该传递参数

    <3>调用时,实参的个数和先后顺序应该和定义函数中要求的一致

    <4>如果调用的函数有返回值,那么就可以用一个变量来进行保存这个值

    3.作用域

    <1>在一个函数中定义的变量,只能在本函数中用(局部变量)

    作业

    必做题

    1.编程实现9*9乘法表

    提示:使用循环嵌套

    2.用函数实现求100-200里面所有的素数

    提示:素数的特征是除了1和其本身能被整除,其它数都不能被整除的数

    3.请用函数实现一个判断用户输入的年份是否是闰年的程序

    提示:
    1.能被400整除的年份 
    2.能被4整除,但是不能被100整除的年份
    以上2种方法满足一种即为闰年

    选做题

    1.用函数实现输入某年某月某日,判断这一天是这一年的第几天?闰年情况也考虑进去

    20160818
    是今年第x

    2.编写“学生管理系统”,要求如下:

    必须使用自定义函数,完成对程序的模块化
    学生信息至少包含:姓名、年龄、学号,除此以外可以适当添加
    必须完成的功能:添加、删除、修改、查询、退出

    <1>打开文件

    在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件

    open(文件名,访问模式)

    示例如下:

     
        f = open('test.txt', 'w')

    说明:

    访问模式

    说明

    r

    以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。

    w

    打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

    a

    打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

    rb

    以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。

    wb

    以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

    ab

    以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

    r+

    打开一个文件用于读写。文件指针将会放在文件的开头。

    w+

    打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

    a+

    打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。

    rb+

    以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。

    wb+

    以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

    ab+

    以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

    <2>关闭文件

    close( )

    示例如下:

     
        # 新建一个文件,文件名为:test.txt
        f = open('test.txt', 'w')
     
        # 关闭这个文件
        f.close()

    文件的读写

    <1>写数据(write)

    使用write()可以完成向文件写入数据

    demo:

     
    f = open('test.txt', 'w')
    f.write('hello world, i am here!')
    f.close()

    运行现象:

    注意:

    • 如果文件不存在那么创建,如果存在那么就先清空,然后写入数据

    <2>读数据(read)

    使用read(num)可以从文件中读取数据,num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据

    demo:

     
    f = open('test.txt', 'r')
     
    content = f.read(5)
     
    print(content)
     
    print("-"*30)
     
    content = f.read()
     
    print(content)
     
    f.close()

    运行现象:

    注意:

    • 如果open是打开一个文件,那么可以不用谢打开的模式,即只写 open('test.txt')
    • 如果使用读了多次,那么后面读取的数据是从上次读完后的位置开始的

    <3>读数据(readlines)

    就像read没有参数时一样,readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素

    #coding=utf-8
     
    f = open('test.txt', 'r')
     
    content = f.readlines()
     
    print(type(content))
     
    i=1
    for temp in content:
        print("%d:%s"%(i, temp))
        i+=1
     
    f.close()

    运行现象:

    <4>读数据(readline)

    #coding=utf-8
     
    f = open('test.txt', 'r')
     
    content = f.readline()
    print("1:%s"%content)
     
    content = f.readline()
    print("2:%s"%content)
     
     
    f.close()

    应用1:制作文件的备份

    任务描述

    • 输入文件的名字,然后程序自动完成对文件进行备份

    参考代码

    #coding=utf-8
     
    oldFileName = input("请输入要拷贝的文件名字:")
     
    oldFile = open(oldFileName,'r')
     
    # 如果打开文件
    if oldFile:
     
        # 提取文件的后缀
        fileFlagNum = oldFileName.rfind('.')
        if fileFlagNum > 0:
            fileFlag = oldFileName[fileFlagNum:]
     
        # 组织新的文件名字
        newFileName = oldFileName[:fileFlagNum] + '[复件]' + fileFlag
     
        # 创建新文件
        newFile = open(newFileName, 'w')
     
        # 把旧文件中的数据,一行一行的进行复制到新文件中
        for lineContent in oldFile.readlines():
            newFile.write(lineContent)
     
        # 关闭文件
        oldFile.close()
        newFile.close()

    <1>获取当前读写的位置

    在读写文件的过程中,如果想知道当前的位置,可以使用tell()来获取

     
        # 打开一个已经存在的文件
        f = open("test.txt", "r")
        str = f.read(3)
        print "读取的数据是 : ", str
     
        # 查找当前位置
        position = f.tell()
        print "当前文件位置 : ", position
     
        str = f.read(3)
        print "读取的数据是 : ", str
     
        # 查找当前位置
        position = f.tell()
        print "当前文件位置 : ", position
     
        f.close()

    <2>定位到某个位置

    如果在读写文件的过程中,需要从另外一个位置进行操作的话,可以使用seek()

    seek(offset, from)有2个参数

    • offset:偏移量
    • from:方向
    • 0:表示文件开头
    • 1:表示当前位置
    • 2:表示文件末尾

    demo:把位置设置为:从文件开头,偏移5个字节

     
        # 打开一个已经存在的文件
        f = open("test.txt", "r")
        str = f.read(30)
        print "读取的数据是 : ", str
     
        # 查找当前位置
        position = f.tell()
        print "当前文件位置 : ", position
     
        # 重新设置位置
        f.seek(5,0)
     
        # 查找当前位置
        position = f.tell()
        print "当前文件位置 : ", position
     
        f.close()

    demo:把位置设置为:离文件末尾,3字节处

     
        # 打开一个已经存在的文件
        f = open("test.txt", "r")
     
        # 查找当前位置
        position = f.tell()
        print "当前文件位置 : ", position
     
        # 重新设置位置
        f.seek(-3,2)
     
        # 读取到的数据为:文件最后3个字节数据
        str = f.read()
        print "读取的数据是 : ", str
     
        f.close()

    文件的重命名、删除

    有些时候,需要对文件进行重命名、删除等一些操作,python的os模块中都有这么功能

    <1>文件重命名

    os模块中的rename()可以完成对文件的重命名操作

    rename(需要修改的文件名, 新的文件名)

     
        import os
     
        os.rename("毕业论文.txt", "毕业论文-最终版.txt")

    <2>删除文件

    os模块中的remove()可以完成对文件的删除操作

    remove(待删除的文件名)

     
        import os
     
        os.remove("毕业论文.txt")

    文件夹的相关操作

    实际开发中,有时需要用程序的方式对文件夹进行一定的操作,比如创建、删除等

    就像对文件操作需要os模块一样,如果要操作文件夹,同样需要os模块

    <1>创建文件夹

     
        import os
     
        os.mkdir("张三")

    <2>获取当前目录

     
        import os
     
        os.getcwd()

    <3>改变默认目录

     
        import os
     
        os.chdir("../")

    <4>获取目录列表

     
        import os
     
        os.listdir("./")

    <5>删除文件夹

     
        import os
     
        os.rmdir("张三")

    <2>参考代码

     
         #coding=utf-8
     
        # 批量在文件名前加前缀
     
        import os
     
        funFlag = 1 # 1表示添加标志  2表示删除标志
     
        folderName = './renameDir/'
     
        # 获取指定路径的所有文件名字
        dirList = os.listdir(folderName)
     
        # 遍历输出所有文件名字
        for name in dirList:
            print name
     
            if funFlag == 1:
                newName = '[东哥出品]-' + name
            elif funFlag == 2:
                num = len('[东哥出品]-')
                newName = name[num:]
            print newName
     
            os.rename(folderName+name, folderName+newName)

    定义类

    定义一个类,格式如下:

     
    class 类名:
        方法列表

    demo:定义一个Car类

    # 定义类
    class Car:
        # 方法
        def getCarInfo(self):
            print('车轮子个数:%d, 颜色%s'%(self.wheelNum, self.color))
     
        def move(self):
            print("车正在移动...")

    说明:

    • 定义类时有2种:新式类和经典类,上面的Car为经典类,如果是Car(object)则为新式类
    • 类名的命名规则按照"大驼峰"

    创建对象

    通过上一节课程,定义了一个Car类;就好比有车一个张图纸,那么接下来就应该把图纸交给生成工人们去生成了

    python中,可以根据已经定义的类去创建出一个个对象

    创建对象的格式为:

    对象名 = 类名()

    创建对象demo:

     
    # 定义类
    class Car:
        # 移动
        def move(self):
            print('车在奔跑...')
     
        # 鸣笛
        def toot(self):
            print("车在鸣笛...嘟嘟..")
     
     
    # 创建一个对象,并用变量BMW来保存它的引用
    BMW = Car()
    BMW.color = '黑色'
    BMW.wheelNum = 4 #轮子数量
    BMW.move()
    BMW.toot()
    print(BMW.color)
    print(BMW.wheelNum)

    总结:

    • BMW = Car(),这样就产生了一个Car的实例对象,此时也可以通过实例对象BMW来访问属性或者方法
    • 第一次使用BMW.color = '黑色'表示给BMW这个对象添加属性,如果后面再次出现BMW.color = xxx表示对属性进行修改
    • BMW是一个对象,它拥有属性(数据)和方法(函数)
    • 当创建一个对象时,就是用一个模子,来制造一个实物 

    __init__()方法

    想一想:

    在上一小节的demo中,我们已经给BMW这个对象添加了2个属性,wheelNum(车的轮胎数量)以及color(车的颜色),试想如果再次创建一个对象的话,肯定也需要进行添加属性,显然这样做很费事,那么有没有办法能够在创建对象的时候,就顺便把车这个对象的属性给设置呢?

    答:

    __init__()方法

    <1>使用方式

    def 类名:
        #初始化函数,用来完成一些默认的设定
        def __init__():
            pass

    <2>__init__()方法的调用

     
    # 定义汽车类
    class Car:
     
        def __init__(self):
            self.wheelNum = 4
            self.color = '蓝色'
     
        def move(self):
            print('车在跑,目标:夏威夷')
     
    # 创建对象
    BMW = Car()
     
    print('车的颜色为:%s'%BMW.color)
    print('车轮胎数量为:%d'%BMW.wheelNum)

    总结1

    当创建Car对象后,在没有调用__init__()方法的前提下,BMW就默认拥有了2个属性wheelNum和color,原因是__init__()方法是在创建对象后,就立刻被默认调用了

    想一想

    既然在创建完对象后__init__()方法已经被默认的执行了,那么能否让对象在调用__init__()方法的时候传递一些参数呢?如果可以,那怎样传递呢?

     
    # 定义汽车类
    class Car:
     
        def __init__(self, newWheelNum, newColor):
            self.wheelNum = newWheelNum
            self.color = newColor
     
        def move(self):
            print('车在跑,目标:夏威夷')
     
    # 创建对象
    BMW = Car(4, 'green')
     
    print('车的颜色为:%s'%BMW.color)
    print('车轮子数量为:%d'%BMW.wheelNum)

    总结2

    • __init__()方法,在创建一个对象时默认被调用,不需要手动调用
    • __init__(self)中,默认有1个参数名字为self,如果在创建对象时传递了2个实参,那么__init__(self)中出了self作为第一个形参外还需要2个形参,例如__init__(self,x,y)
    • __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递进去

    2. 定义__str__()方法

    class Car:
     
        def __init__(self, newWheelNum, newColor):
            self.wheelNum = newWheelNum
            self.color = newColor
     
        def __str__(self):
            msg = "嘿。。。我的颜色是" + self.color + "我有" + int(self.wheelNum) + "个轮胎..."
            return msg
     
        def move(self):
            print('车在跑,目标:夏威夷')
     
     
    BMW = Car(4, "白色")
    print(BMW)

    self

    1.理解self

    看如下示例:

    # 定义一个类
    class Animal:
     
        # 方法
        def __init__(self, name):
            self.name = name
     
        def printName(self):
            print('名字为:%s'%self.name)
     
    # 定义一个函数
    def myPrint(animal):
        animal.printName()
     
     
    dog1 = Animal('西西')
    myPrint(dog1)
     
    dog2 = Animal('北北')
    myPrint(dog2)

    运行结果:

    应用:烤地瓜

    为了更好的理解面向对象编程,下面以“烤地瓜”为案例,进行分析

    1.分析“烤地瓜”的属性和方法

    示例属性如下:

    • cookedLevel : 这是数字;0~3表示还是生的,超过3表示半生不熟,超过5表示已经烤好了,超过8表示已经烤成木炭了!我们的地瓜开始时时生的
    • cookedString : 这是字符串;描述地瓜的生熟程度
    • condiments :这是地瓜的配料列表,比如番茄酱、芥末酱等
    • cook() : 把地瓜烤一段时间
    • addCondiments() : 给地瓜添加配料
    • __init__() : 设置默认的属性
    • __str__() : 让print的结果看起来更好一些

    示例方法如下:

    2. 定义类,并且定义__init__()方法

    #定义`地瓜`类
    class SweetPotato:
        '这是烤地瓜的类'
     
        #定义初始化方法
        def __init__(self):
            self.cookedLevel = 0
            self.cookedString = "生的"
            self.condiments = []

    3.添加"烤地瓜"方法

        #烤地瓜方法
        def cook(self, time):
            self.cookedLevel += time
            if self.cookedLevel > 8:
                self.cookedString = "烤成灰了"
            elif self.cookedLevel > 5:
                self.cookedString = "烤好了"    
            elif self.cookedLevel > 3:
                self.cookedString = "半生不熟"
            else:
                self.cookedString = "生的"

    4.基本的功能已经有了一部分,赶紧测试一下

    把上面2块代码合并为一个程序后,在代码的下面添加以下代码进行测试

    mySweetPotato = SweetPotato()
    print(mySweetPotato.cookedLevel)
    print(mySweetPotato.cookedString)
    print(mySweetPotato.condiments)

    完整的代码为:

    class SweetPotato:
        '这是烤地瓜的类'
     
        #定义初始化方法
        def __init__(self):
            self.cookedLevel = 0
            self.cookedString = "生的"
            self.condiments = []
     
            #烤地瓜方法
        def cook(self, time):
            self.cookedLevel += time
            if self.cookedLevel > 8:
                self.cookedString = "烤成灰了"
            elif self.cookedLevel > 5:
                self.cookedString = "烤好了"    
            elif self.cookedLevel > 3:
                self.cookedString = "半生不熟"
            else:
                self.cookedString = "生的"
     
    # 用来进行测试
    mySweetPotato = SweetPotato()
    print(mySweetPotato.cookedLevel)
    print(mySweetPotato.cookedString)
    print(mySweetPotato.condiments)

    5.测试cook方法是否好用

    在上面的代码最后面添加如下代码:

    print("------接下来要进行烤地瓜了-----")
    mySweetPotato.cook(4) #烤4分钟
    print(mySweetPotato.cookedLevel)
    print(mySweetPotato.cookedString)

    6. 定义addCondiments()方法和__str__()方法

     
        def __str__(self):
            msg = self.cookedString + " 地瓜"
            if len(self.condiments) > 0:
                msg = msg + "("
                for temp in self.condiments:
                    msg = msg + temp + ", "
                msg = msg.strip(", ")
     
                msg = msg + ")"
            return msg
     
        def addCondiments(self, condiments):
            self.condiments.append(condiments)

    7.再次测试

    完整的代码如下:

     
    class SweetPotato:
        "这是烤地瓜的类"
     
        #定义初始化方法
        def __init__(self):
            self.cookedLevel = 0
            self.cookedString = "生的"
            self.condiments = []
     
        #定制print时的显示内容
        def __str__(self):
            msg = self.cookedString + " 地瓜"
            if len(self.condiments) > 0:
                msg = msg + "("
     
                for temp in self.condiments:
                    msg = msg + temp + ", "
                msg = msg.strip(", ")
     
                msg = msg + ")"
            return msg
     
        #烤地瓜方法
        def cook(self, time):
            self.cookedLevel += time
            if self.cookedLevel > 8:
                self.cookedString = "烤成灰了"
            elif self.cookedLevel > 5:
                self.cookedString = "烤好了"    
            elif self.cookedLevel > 3:
                self.cookedString = "半生不熟"
            else:
                self.cookedString = "生的"
     
        #添加配料
        def addCondiments(self, condiments):
            self.condiments.append(condiments)
     
    # 用来进行测试
    mySweetPotato = SweetPotato()
    print("------有了一个地瓜,还没有烤-----")
    print(mySweetPotato.cookedLevel)
    print(mySweetPotato.cookedString)
    print(mySweetPotato.condiments)
    print("------接下来要进行烤地瓜了-----")
    print("------地瓜经烤了4分钟-----")
    mySweetPotato.cook(4) #烤4分钟
    print(mySweetPotato)
    print("------地瓜又经烤了3分钟-----")
    mySweetPotato.cook(3) #又烤了3分钟
    print(mySweetPotato)
    print("------接下来要添加配料-番茄酱------")
    mySweetPotato.addCondiments("番茄酱")
    print(mySweetPotato)
    print("------地瓜又经烤了5分钟-----")
    mySweetPotato.cook(5) #又烤了5分钟
    print(mySweetPotato)
    print("------接下来要添加配料-芥末酱------")
    mySweetPotato.addCondiments("芥末酱")
    print(mySweetPotato)

    隐藏数据

    可能你已经意识到,查看过着修改对象的属性(数据),有2种方法

    1.直接通过对象名修改

        SweetPotato.cookedLevel = 5

    2.通过方法间接修改

        SweetPotato.cook(5)

    分析

    明明可以使用第1种方法直接修改,为什么还要定义方法来间接修改呢?

    至少有2个原因:

    • 如果直接修改属性,烤地瓜至少需要修改2部分,即修改cookedLevel和cookedString。而使用方法来修改时,只需要调用一次即可完成
    • 如果直接访问属性,可能会出现一些数据设置错误的情况产生例如cookedLevel = -3。这会使地瓜比以前还生,当然了这也没有任何意义,通过使用方法来进行修改,就可以在方法中进行数据合法性的检查

    应用:存放家具

    #定义一个home类
    class Home:
     
        def __init__(self, area):
            self.area = area #房间剩余的可用面积
            #self.light = 'on' #灯默认是亮的
            self.containsItem = []
     
        def __str__(self):
            msg = "当前房间可用面积为:" + str(self.area)
            if len(self.containsItem) > 0:
                msg = msg + " 容纳的物品有: "
                for temp in self.containsItem:
                    msg = msg + temp.getName() + ", "
                msg = msg.strip(", ")
            return msg
     
        #容纳物品
        def accommodateItem(self,item):
            #如果可用面积大于物品的占用面积
            needArea = item.getUsedArea()
            if self.area > needArea:
                self.containsItem.append(item)
                self.area -= needArea
                print("ok:已经存放到房间中")
            else:
                print("err:房间可用面积为:%d,但是当前要存放的物品需要的面积为%d"%(self.area, needArea))
     
     
    #定义bed类
    class Bed:
     
        def __init__(self,area,name = '床'):
            self.name = name
            self.area = area
     
        def __str__(self):
            msg = '床的面积为:' + str(self.area)
            return msg
     
        #获取床的占用面积
        def getUsedArea(self):
            return self.area
     
        def getName(self):
            return self.name
     
     
    #创建一个新家对象
    newHome = Home(100)#100平米
    print(newHome)
     
    #创建一个床对象
    newBed = Bed(20)
    print(newBed)
     
    #把床安放到家里
    newHome.accommodateItem(newBed)
    print(newHome)
     
    #创建一个床对象
    newBed2 = Bed(30,'席梦思')
    print(newBed2)
     
    #把床安放到家里
    newHome.accommodateItem(newBed2)
    print(newHome)

    总结:

    • 如果一个对象与另外一个对象有一定的关系,那么一个对象可用是另外一个对象的属性
    • 添加“开、关”灯,让房间、床一起亮、灭

    思维升华:

    1.人类

    • 属性
    • 姓名
    • 血量
    • 持有的枪
    • 方法
    • 安子弹
    • 安弹夹
    • 拿枪(持有抢)
    • 开枪
    • 属性
    • 杀伤力
    • 方法
    • 伤害敌人(让敌人掉血)
    • 属性
    • 容量(子弹存储的最大值)
    • 当前保存的子弹
    • 方法
    • 保存子弹(安装子弹的时候)
    • 弹出子弹(开枪的时候)
    • 属性
    • 弹夹(默认没有弹夹,需要安装)
    • 方法
    • 连接弹夹(保存弹夹)
    • 射子弹

    2.子弹类

    3.弹夹类

    4.枪类

    参考代码

    #人类
    class Ren:
        def __init__(self,name):
            self.name = name
            self.xue = 100
            self.qiang = None
     
        def __str__(self):
            return self.name + "剩余血量为:" + str(self.xue)
     
        def anzidan(self,danjia,zidan):
            danjia.baocunzidan(zidan)
     
        def andanjia(self,qiang,danjia):
            qiang.lianjiedanjia(danjia)
     
        def naqiang(self,qiang):
            self.qiang = qiang
     
        def kaiqiang(self,diren):
            self.qiang.she(diren)
     
        def diaoxue(self,shashangli):
            self.xue -= shashangli
     
    #弹夹类
    class Danjia:
        def __init__(self, rongliang):
            self.rongliang = rongliang
            self.rongnaList = []
     
        def __str__(self):
            return "弹夹当前的子弹数量为:" + str(len(self.rongnaList)) + "/" + str(self.rongliang)
     
        def baocunzidan(self,zidan):
            if len(self.rongnaList) < self.rongliang:
                self.rongnaList.append(zidan)
     
        def chuzidan(self):
            #判断当前弹夹中是否还有子弹
            if len(self.rongnaList) > 0:
                #获取最后压入到单间中的子弹
                zidan = self.rongnaList[-1]
                self.rongnaList.pop()
                return zidan
            else:
                return None
     
    #子弹类
    class Zidan:
        def __init__(self,shashangli):
            self.shashangli = shashangli
     
        def shanghai(self,diren):
            diren.diaoxue(self.shashangli)
     
    #枪类
    class Qiang:
        def __init__(self):
            self.danjia = None
     
        def __str__(self):
            if self.danjia:
                return "枪当前有弹夹"
            else:
                return "枪没有弹夹"
     
        def lianjiedanjia(self,danjia):
            if not self.danjia:
                self.danjia = danjia
     
     
        def she(self,diren):
            zidan = self.danjia.chuzidan()
            if zidan:
                zidan.shanghai(diren)
            else:
                print("没有子弹了,放了空枪....")
     
     
    #创建一个人对象
    laowang = Ren("老王")
     
    #创建一个弹夹
    danjia = Danjia(20)
    print(danjia)
     
    #循环的方式创建一颗子弹,然后让老王把这颗子弹压入到弹夹中
    i=0
    while i<5:
        zidan = Zidan(5)
        laowang.anzidan(danjia,zidan)
        i+=1
    #测试一下,安装完子弹后,弹夹中的信息
    print(danjia)
     
    #创建一个枪对象
    qiang = Qiang()
    print(qiang)
    #让老王,把弹夹连接到枪中
    laowang.andanjia(qiang,danjia)
    print(qiang)
     
     
    #创建一个敌人
    diren = Ren("敌人")
    print(diren)
     
    #让老王拿起枪
    laowang.naqiang(qiang)
     
    #老王开枪射敌人
    laowang.kaiqiang(diren)
    print(diren)
    print(danjia)
     
    laowang.kaiqiang(diren)
    print(diren)
    print(danjia)

    第三节项目飞机大战

    保护对象的属性

    如果有一个对象,当需要对其进行修改属性时,有2种方法

    • 对象名.属性名= 数据---->直接修改
    • 对象名.方法名() ---->间接修改

    为了更好的保存属性安全,即不能随意修改,一般的处理方式为

    • 将属性定义为私有属性
    • 添加一个可以调用的方法,供调用
    class People(object):
     
        def __init__(self, name):
            self.__name = name
     
        def getName(self):
            return self.__name
     
        def setName(self, newName):
            if len(newName) >= 5:
                self.__name = newName
            else:
                print("error:名字长度需要大于或者等于5")
     
    xiaoming = People("dongGe")
    print(xiaoming.__name)
    class People(object):
     
        def __init__(self, name):
            self.__name = name
     
        def getName(self):
            return self.__name
     
        def setName(self, newName):
            if len(newName) >= 5:
                self.__name = newName
            else:
                print("error:名字长度需要大于或者等于5")
     
    xiaoming = People("dongGe")
     
    xiaoming.setName("wanger")
    print(xiaoming.getName())
     
    xiaoming.setName("lisi")
    print(xiaoming.getName())

    总结

    • Python中没有像C++中public和private这些关键字来区别公有属性和私有属性
    • 它是以属性命名方式来区分,如果在属性名前面加了2个下划线'__',则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。

    __del__()方法

    创建对象后,python解释器默认调用__init__()方法;

    当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法

     
    import time
    class Animal(object):
     
        # 初始化方法
        # 创建完对象后会自动被调用
        def __init__(self, name):
            print('__init__方法被调用')
            self.__name = name
     
     
        # 析构方法
        # 当对象被删除时,会自动被调用
        def __del__(self):
            print("__del__方法被调用")
            print("%s对象马上被干掉了..."%self.__name)
     
    # 创建对象
    dog = Animal("哈皮狗")
     
    # 删除对象
    del dog
     
     
    cat = Animal("波斯猫")
    cat2 = cat
    cat3 = cat
     
    print("---马上 删除cat对象")
    del cat
    print("---马上 删除cat2对象")
    del cat2
    print("---马上 删除cat3对象")
    del cat3
     
    print("程序2秒钟后结束")
    time.sleep(2)

    结果:

    总结

    • 当有1个变量保存了对象的引用时,此对象的引用计数就会加1
    • 当使用del删除变量指向的对象时,如果对象的引用计数不会1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次del,此时会真的把对象进行删除

    2.继承示例

    # 定义一个父类,如下:
    class Cat(object):
     
        def __init__(self, name, color="白色"):
            self.name = name
            self.color = color
     
        def run(self):
            print("%s--在跑"%self.name)
     
     
    # 定义一个子类,继承Cat类如下:
    class Bosi(Cat):
     
        def setNewName(self, newName):
            self.name = newName
     
        def eat(self):
            print("%s--在吃"%self.name)
     
     
    bs = Bosi("印度猫")
    print('bs的名字为:%s'%bs.name)
    print('bs的颜色为:%s'%bs.color)
    bs.eat()
    bs.setNewName('波斯')
    bs.run()

    运行结果:

    说明:

    • 虽然子类没有定义__init__方法,但是父类有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建Bosi的对象,就默认执行了那个继承过来的__init__方法
    • 子类在继承的时候,在定义类时,小括号()中为父类的名字
    • 父类的属性、方法,会被继承给子类
    • 私有的属性,不能通过对象直接访问,但是可以通过方法访问
    • 私有的方法,不能通过对象直接访问
    • 私有的属性、方法,不会被子类继承,也不能被访问
    • 一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用

    总结

    3.注意点

    class Animal(object):
     
        def __init__(self, name='动物', color='白色'):
            self.__name = name
            self.color = color
     
        def __test(self):
            print(self.__name)
            print(self.color)
     
        def test(self):
            print(self.__name)
            print(self.color)
     
     
     
    class Dog(Animal):
        def dogTest1(self):
            #print(self.__name) #不能访问到父类的私有属性
            print(self.color)
     
     
        def dogTest2(self):
            #self.__test() #不能访问父类中的私有方法
            self.test()
     
     
    A = Animal()
    #print(A.__name) #程序出现异常,不能访问私有属性
    print(A.color)
    #A.__test() #程序出现异常,不能访问私有方法
    A.test()
     
    print("------分割线-----")
     
    D = Dog(name = "小花狗", color = "黄色")
    D.dogTest1()
    D.dogTest2()

    Python中多继承的格式如下:

     
    # 定义一个父类
    class A:
        def printA(self):
            print('----A----')
     
    # 定义一个父类
    class B:
        def printB(self):
            print('----B----')
     
    # 定义一个子类,继承自A、B
    class C(A,B):
        def printC(self):
            print('----C----')
     
    obj_C = C()
    obj_C.printA()
    obj_C.printB()

    运行结果:

    ----A----
    ----B----

    说明

    • python中是可以多继承的
    • 父类中的方法、属性,子类会继承
    • 想一想:

    注意点

    如果在上面的多继承例子中,如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?

    #coding=utf-8
    class base(object):
        def test(self):
            print('----base test----')
    class A(base):
        def test(self):
            print('----A test----')
     
    # 定义一个父类
    class B(base):
        def test(self):
            print('----B test----')
     
    # 定义一个子类,继承自A、B
    class C(A,B):
        pass
     
     
    obj_C = C()
    obj_C.test()
     
    print(C.__mro__) #可以查看C类的对象搜索方法时的先后顺序

    重写父类方法与调用父类方法

    1.重写父类方法

    所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法

    #coding=utf-8
    class Cat(object):
        def sayHello(self):
            print("halou-----1")
     
     
    class Bosi(Cat):
     
        def sayHello(self):
            print("halou-----2")
     
    bosi = Bosi()
     
    bosi.sayHello()

    2.调用父类的方法

    #coding=utf-8
    class Cat(object):
        def __init__(self,name):
            self.name = name
            self.color = 'yellow'
     
     
    class Bosi(Cat):
     
        def __init__(self,name):
            # 调用父类的__init__方法1(python2)
            #Cat.__init__(self,name)
            # 调用父类的__init__方法2
            #super(Bosi,self).__init__(name)
            # 调用父类的__init__方法3
            super().__init__(name)
     
        def getName(self):
            return self.name
     
    bosi = Bosi('xiaohua')
     
    print(bosi.name)
    print(bosi.color)

    多态

    多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。

    所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态

    • Python伪代码实现Java或C#的多态
    • Python “鸭子类型”
     
    class F1(object):
        def show(self):
            print 'F1.show'
     
    class S1(F1):
        def show(self):
            print 'S1.show'
     
    class S2(F1):
        def show(self):
            print 'S2.show'
     
    # 由于在Java或C#中定义函数参数时,必须指定参数的类型
    # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
    # 而实际传入的参数是:S1对象和S2对象
     
    def Func(F1 obj):
        """Func函数需要接收一个F1类型或者F1子类的类型"""
     
        print obj.show()
     
    s1_obj = S1()
    Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show
     
    s2_obj = S2()
    Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show
    class F1(object):
        def show(self):
            print 'F1.show'
     
    class S1(F1):
     
        def show(self):
            print 'S1.show'
     
    class S2(F1):
     
        def show(self):
            print 'S2.show'
     
    def Func(obj):
        print obj.show()
     
    s1_obj = S1()
    Func(s1_obj) 
     
    s2_obj = S2()
    Func(s2_obj)

    类属性、实例属性

    在了解了类基本的东西之后,下面看一下python中这几个概念的区别

    先来谈一下类属性实例属性

    在前面的例子中我们接触到的就是实例属性(对象属性),顾名思义,类属性就是类对象所拥有的属性,它被所有类对象实例对象所共有,在内存中只存在一个副本,这个和C++中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象实例对象访问

    类属性

    class People(object):
        name = 'Tom'  #公有的类属性
        __age = 12     #私有的类属性
     
    p = People()
     
    print(p.name)           #正确
    print(People.name)      #正确
    print(p.__age)            #错误,不能在类外通过实例对象访问私有的类属性
    print(People.__age)        #错误,不能在类外通过类对象访问私有的类属性

    实例属性(对象属性)

    class People(object):
        address = '山东' #类属性
        def __init__(self):
            self.name = 'xiaowang' #实例属性
            self.age = 20 #实例属性
     
    p = People()
    p.age =12 #实例属性
    print(p.address) #正确
    print(p.name)    #正确
    print(p.age)     #正确
     
    print(People.address) #正确
    print(People.name)    #错误
    print(People.age)     #错误

    通过实例(对象)去修改类属性

    class People(object):
        country = 'china' #类属性
     
     
    print(People.country)
    p = People()
    print(p.country)
    p.country = 'japan' 
    print(p.country)      #实例属性会屏蔽掉同名的类属性
    print(People.country)
    del p.country    #删除实例属性
    print(p.country)

    总结

    • 如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性

    静态方法和类方法

    1.类方法

    是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。

    class People(object):
        country = 'china'
     
        #类方法,用classmethod来进行修饰
        @classmethod
        def getCountry(cls):
            return cls.country
     
    p = People()
    print p.getCountry()    #可以用过实例对象引用
    print People.getCountry()    #可以通过类对象引用

    类方法还有一个用途就是可以对类属性进行修改:

    class People(object):
        country = 'china'
     
        #类方法,用classmethod来进行修饰
        @classmethod
        def getCountry(cls):
            return cls.country
     
        @classmethod
        def setCountry(cls,country):
            cls.country = country
     
     
    p = People()
    print p.getCountry()    #可以用过实例对象引用
    print People.getCountry()    #可以通过类对象引用
     
    p.setCountry('japan')   
     
    print p.getCountry()   
    print People.getCountry()

    结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变

    2.静态方法

    需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数

    class People(object):
        country = 'china'
     
        @staticmethod
        #静态方法
        def getCountry():
            return People.country
     
     
    print People.getCountry()

    总结

    从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用

    练习:设计类

    1.设计一个卖车的4S店,该怎样做呢?

     
    # 定义车类
    class Car(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义一个销售车的店类
    class CarStore(object):
     
        def order(self):
            self.car = Car() #找一辆车
            self.car.move()
            self.car.stop()

    说明

    上面的4s店,只能销售那一种类型的车

    如果这个是个销售北京现代品牌的车,比如伊兰特、索纳塔等,该怎样做呢?

    2.设计一个卖北京现代车的4S店

    # 定义伊兰特车类
    class YilanteCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义索纳塔车类
    class SuonataCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义一个销售北京现代车的店类
    class CarStore(object):
     
        def order(self, typeName):
            #根据客户的不同要求,生成不同的类型的车
            if typeName == "伊兰特":
                car = YilanteCar()
            elif typeName == "索纳塔":
                car = SuonataCar()
            return car

    这样做,不太好,因为当北京现代又生产一种新类型的车时,又得在CarStore类中修改,有没有好的解决办法呢?

    工厂模式

    1. 简单工厂模式

    在上一节中,最后留下的个问题,该怎样解决呢?

    1.1.使用函数实现

     
    # 定义伊兰特车类
    class YilanteCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义索纳塔车类
    class SuonataCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 用来生成具体的对象
    def createCar(typeName):
        if typeName == "伊兰特":
            car = YilanteCar()
        elif typeName == "索纳塔":
            car = SuonataCar()
        return car
     
    # 定义一个销售北京现代车的店类
    class CarStore(object):
     
        def order(self, typeName):
            # 让工厂根据类型,生产一辆汽车
            car = createCar(typeName)
            return car

    1.2.使用类来实现

     
    # 定义伊兰特车类
    class YilanteCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义索纳塔车类
    class SuonataCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义一个生产汽车的工厂,让其根据具体的订单生产车
    class CarFactory(object):
     
        def createCar(self,typeName):
            if typeName == "伊兰特":
                car = YilanteCar()
            elif typeName == "索纳塔":
                car = SuonataCar()
     
            return car
     
    # 定义一个销售北京现代车的店类
    class CarStore(object):
     
        def __init__(self):
            #设置4s店的指定生产汽车的工厂
            self.carFactory = CarFactory()
     
        def order(self, typeName):
            # 让工厂根据类型,生产一辆汽车
            car = self.carFactory.createCar(typeName)
            return car

    咋一看来,好像只是把生产环节重新创建了一个类,这确实比较像是一种编程习惯,此种解决方式被称作简单工厂模式

    工厂函数、工厂类对具体的生成环节进行了封装,这样有利于代码的后需扩展,即把功能划分的更具体,4s店只负责销售,汽车厂只负责制造

    2. 工厂方法模式

    多种品牌的汽车4S店

    当买车时,有很多种品牌可以选择,比如北京现代、别克、凯迪拉克、特斯拉等,那么此时该怎样进行设计呢?

     
    # 定义一个基本的4S店类
    class CarStore(object):
     
        #仅仅是定义了有这个方法,并没有实现,具体功能,这个需要在子类中实现
        def createCar(self, typeName):
            pass
     
        def order(self, typeName):
            # 让工厂根据类型,生产一辆汽车
            self.car = self.createCar(typeName)
            self.car.move()
            self.car.stop()
     
    # 定义一个北京现代4S店类
    class XiandaiCarStore(CarStore):
     
        def createCar(self, typeName):
            self.carFactory = CarFactory()
            return self.carFactory.createCar(typeName)
     
     
    # 定义伊兰特车类
    class YilanteCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义索纳塔车类
    class SuonataCar(object):
     
        # 定义车的方法
        def move(self):
            print("---车在移动---")
     
        def stop(self):
            print("---停车---")
     
    # 定义一个生产汽车的工厂,让其根据具体得订单生产车
    class CarFactory(object):
     
        def createCar(self,typeName):
            self.typeName = typeName
            if self.typeName == "伊兰特":
                self.car = YilanteCar()
            elif self.typeName == "索纳塔":
                self.car = SuonataCar()
     
            return self.car
     
    suonata = XiandaiCarStore()
    suonata.order("索纳塔")

    最后来看看工厂方法模式的定义

    定义了一个创建对象的接口(可以理解为函数),但由子类决定要实例化的类是哪一个,工厂方法模式让类的实例化推迟到子类,抽象的CarStore提供了一个创建对象的方法createCar,也叫作工厂方法

    子类真正实现这个createCar方法创建出具体产品。创建者类不需要直到实际创建的产品是哪一个,选择了使用了哪个子类,自然也就决定了实际创建的产品是什么。

    。。。。。。。。

  • 相关阅读:
    第四章5
    第四章4
    第四章3
    第四章2
    第四章1
    第四章例4-8
    第四章例4-7
    第四章例4-6
    第四章例4-5
    第四章例4-4
  • 原文地址:https://www.cnblogs.com/wanglinjie/p/9107967.html
Copyright © 2020-2023  润新知