【Python语言速回顾】——函数&模块&类与对象

目录

引入

一、函数

1、函数概述

2、参数和返回值

3、函数的调用

二、模块

1、模块概述

2、模块应用实例

三、类与对象

1、面向对象概述

2、类

3、类的特点


引入

为了使程序实现的代码更加简单。需要把程序分成越来越小的组成部分,3种方式——函数、对象、模块。


一、函数

1、函数概述

C语言大括号{}里是函数体,python种缩进块里是函数体(配合def和冒号:)
函数外面定义的变量是全局变量(大写配合下划线)
函数内部定义的局部变量(小写配合下划线),只在内部有效

def foodprice(per_price,number):
    sum_price = per_price*number
    return sum_price
PER_PRICE = float(input('请输入单价:'))
NUMBER = float(input('请输入数量:'))
SUM_PRICE = foodprice(PER_PRICE,NUMBER)
print('一共',SUM_PRICE,'元')

==========运行结果==========
请输入单价:15
请输入数量:4
一共 60.0 元

若想在函数内部修改全局变量,并使之在整个程序生效用关键字global:

def foodprice(per_price,number):
    global PER_PRICE
    PER_PRICE = 10
    sum_price = per_price*number
    return sum_price

2、参数和返回值

在python种,函数参数分为3种:位置参数、可变参数、关键字参数。参数的类型不同,参数的传递方式也不同。

①位置参数
位置参数:传入参数值按照位置顺序依次赋给参数。
传递方式:直接传入参数即可,如果有多个参数,位置先后顺序不可改变。若交换顺序,函数结果则不同

def sub(x,y):
    return x-y
>>> sub(10,5)
5
>>> sub(5,10)
-5

②关键字参数
关键字参数:通过参数名指定需要赋值的参数,可忽略参数顺序
传递方式:2种,一是直接传入参数,二是先将参数封装成字典,再在封装后的字典前添加两个星号**传入

>>> sub(y=5,x=10)
5
>>> sub(**{'y':5,'x':10})
5

③默认值参数
当函数调用忘记给某个参数值时,会使用默认值代替(以防报错)

def sub(x=100,y=50):
    return x-y
>>> sub()
50
>>> sub(51)
1
>>> sub(10,5)
5

④可变参数
可变参数:在定义函数参数时,我们不知道到底需要几个参数,只要在参数前面加上星号*即可。

def var(*param):
    print('可变参数param中第3个参数是:',param[2])
    print('可变参数param的长度是:',len(param))
>>> var('BUCT',1958,'Liming',100,'A')
可变参数param中第3个参数是: Liming
可变参数param的长度是: 5

除了可变参数,后也可有普通参数(普参需要用关键字参数传值):

def var(*param,str1):
    print('可变参数param中第3个参数是:',param[2])
    print('可变参数param的长度是:',len(param))
    print('我是普通参数:',str1)
>>> var('BUCT',1958,'Liming',100,'A',str1 = '普参')
可变参数param中第3个参数是: Liming
可变参数param的长度是: 5
我是普通参数: 普参

⑤函数的返回值
若没有用return指定返回值,则返回一个空None

result = var('BUCT',1958,'Liming',100,'A',str1 = '普参')
>>> print(result)
None

若将函数改为:

def var(*param,str1):
    print('可变参数param中第3个参数是:',param[2])
    print('可变参数param的长度是:',len(param))
    print('我是普通参数:',str1)
    return param
>>> print(result)
('BUCT', 1958, 'Liming', 100, 'A')


3、函数的调用

自定义函数:先定义再调用
内置函数:直接调用,有的在特定模块里,需要先import相应模块

①嵌套调用
内嵌函数/内部函数:在函数内部再定义一个函数,作用域只在其相邻外层函数缩进块内部。

②使用闭包
闭包:函数式编程的一个重要的语法结构,在一个内部函数里对外部作用域(不是全局作用域)的变量进行引用。此时这个内部函数叫做闭包函数,如下sub2(b)就是引用了a,为闭包函数:

def sub(a):
    def sub2(b):
        result = a-b
        return result
    return sub2
print(sub(10)(5))
运行结果:5

注:闭包本质还是内嵌函数,不能再全局域访问,外部函数sub的局部变量对于闭包来说是全局变量,可以访问但是不能修改。

③递归调用
递归:严格说其属于算法范畴,不属于语法范围。函数调用自身的行为叫做递归。
两个条件:调用函数自身、设置了正确的返回条件。如计算正整数N的阶乘: 

常规迭代算法:

def factorial(n):
    result = n
    for i in range(1,n):
        result *= i
    return result
print(factorial(10))
运行结果:3628800

迭代算法:

def factorial(n):
    if n == 1:
        return 1
    else:
        return n*factorial(n-1)
print(factorial(10))
运行结果:3628800

注:python默认递归深度为100层(限制),也可用sys.setrecursionlimit(x)指定递归深度。递归有危险(消耗时间和空间),因为它是基于弹栈和出栈操作;递归忘记返回时/未设置正确返回条件时,会使程序崩溃,消耗掉所有内存。


二、模块

1、模块概述

模块实际上是一种更为高级的封装。前面容器(元组,列表)是对数据的封装,函数是对语句的封装,类是方法和属性的封装,模块就是对程序的封装。——就是我们保存的实现特定功能的.py文件

命名空间:一个包含了一个或多个变量名称和它们各自对应的对象值的字典,python可调用局部命名空间和全局命名空间中的变量。
如果一个局部变量与全局变量重名,则再函数内部调用局部变量时,会屏蔽全局变量。
如果要修改函数内全局变量的值,需要借助global

①模块导入方法
方法一:

import modulename
import modulename1,modulename2

使用函数时modulename.functionname()即可

方法二:

from  modulename  import functionname1,functionname2

方法三:

import modulename as newname

相当于给模块起了个新名字,便于记忆也方便调用

②自定义模块和包

自定义模块:
编写的mine.py文件放在与调用程序同一目录下,在其他文件中使用时就可import mine使用。

自定义包:
在大型项目开发中,为了避免模块名重复,python引入了按目录来组织模块的方法,称为包(package)。
包是一个分层级的文件目录结构,定义了有模块、子包、子包下的子包等组成的命名空间。
只要顶层报名不与其他人重名,内部的所有模块都不会冲突。——P114

③安装第三方包
pip install xxxx

2、模块应用实例

①日期和时间相关:datatime模块
导入:

from datetime import datetime
#不能import datetime,可以import datetime.datetime(因为datetime模块里还有一个datetime类)

获取当前日期时间:

>>> now = datetime.now()
>>> print(now)
2023-10-05 17:28:24.303285

获取指定日期时间:

>>> dt = datetime(2020,12,12,11,30,45)
>>> print(dt)
2020-12-12 11:30:45

datetime与timestamp互相转换:
把1970-1-1 00:00:00 UTC+00:00的时间作为epoch time,记为0,当前时间就是相对于epoch time的秒数,称为timestamp。
计算机中存储的当前时间是以timestamp表示的,与时区无关,全球各地计算机在任意时刻的timestamp是相同的。

>>> dt = dt.timestamp()
>>> print(dt)
1607743845.0
#再转换回去:
>>> dt = datetime.fromtimestamp(dt)
>>> print(dt)
2020-12-12 11:30:45

str转换为datetime:
用户输入的日期和时间类型是字符串,要处理日期和时间,必须把str转换为datetime

>>> test = datetime.strptime('2023-10-05  17:49:00','%Y-%m-%d %H:%M:%S')  #特殊字符规定了格式
>>> print(test)
2023-10-05 17:49:00

datetime转换为str:
若已有datetime对象,要把它格式化为字符才能显示给用户

>>> now = datetime.now()
>>> print(now.strftime('%a,%b %d %H:%M'))
Thu,Oct 05 17:28

datetime加减计算:(再导入timedelta类)

>>> from datetime import datetime,timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2023, 10, 5, 17, 58, 9, 377124)
>>> now + timedelta(hours = 2)
datetime.datetime(2023, 10, 5, 19, 58, 9, 377124)
>>> now - timedelta(days = 3)
datetime.datetime(2023, 10, 2, 17, 58, 9, 377124)

本地时间转换为UTC时间:(再导入timedelta、timezone类)
本地时间为系统设定时区的时间,例如北京时间是UTC+8:00时区的时间,而UTC时间指UTC+0:00时区的时间。datetime里面有时区属性tzinfo。

>>> from datetime import datetime,timedelta,timezone
>>> new_utc = timezone(timedelta(hours = 8)) #创建新时区UTC+8:00
>>> new_utc1 = timezone(timedelta(hours = 7)) #创建新时区UTC+7:00
>>> now = datetime.now()
>>> now
datetime.datetime(2023, 10, 5, 18, 9, 17, 667655)
>>> test = now.replace(tzinfo = new_utc) #为当前时间强制设置新的时区
>>> test
datetime.datetime(2023, 10, 5, 18, 9, 17, 667655, tzinfo=datetime.timezone(datetime.timedelta(0, 28800)))
>>> test1 = now.replace(tzinfo = new_utc1) #为当前时间强制设置新的时区UTC+7:00
>>> test1
datetime.datetime(2023, 10, 5, 18, 9, 17, 667655, tzinfo=datetime.timezone(datetime.timedelta(0, 25200)))

时区转换:
先用datetime类提供的utcnow()方法获取当前UTC时间,再用astimezone()方法转换为任意时区的时间

②读写JSON数据:json模块
JSON是一种轻量级的数据交换格式,等同于python里面的字典格式,里面可以包含方括号括起来的数组(列表)。json模块专门解决json格式的数据,提供4种方法:dumps()、dump()、loads()、load()
dumps()、dump()实现序列化功能:

dumps()实现将数据序列化为字符串str,而使用dump()时必须传文件描述符,将序列化的字符串str保存到文件中。

>>> import json
>>> json.dumps('huang')
'"huang"'
>>> json.dumps(13.14)
'13.14'
>>> dict1 = {'name':'huang','school':'buct'}
>>> json.dumps(dict1)
'{"name": "huang", "school": "buct"}'

>>> with open("D:\\json_test.json","w",encoding = 'utf-8')as file_test:
    json.dump(dict1,file_test,indent = 4)
运行结果:dump()方法将字典数据dict_test保存到D盘文件夹下的json_test.json文件中,里面的内容如下
{
    "name": "huang",
    "school": "buct"
}

loads()、load()是反序列化方法:

loads()只完成了反序列化,load()只接收文件描述符,完成了读取文件和反序列化。

>>> json.loads(json.dumps(dict1))  #loads直接操作程序中的字典
{'name': 'huang', 'school': 'buct'}   #dumps将字典序列化成str,loads又使其恢复字典身份

>>> with open("D:\\json_test.json","r",encoding = 'utf-8')as file_test:  #读刚才保存的序列化str字符串数据
    test_loads = json.loads(file_test.read())  #用loads实现反序列化
    file_test.seek(0) #重新定位在文件的第0位及开始位置
    test_load = json.load(file_test)  #用load实现反序列化
>>> print(test_loads)
{'name': 'huang', 'school': 'buct'}
>>> print(test_load)
{'name': 'huang', 'school': 'buct'}

③系统相关:sys模块
sys是python自带模块,包含了与系统相关的信息。可通过help(sys)或dir(sys)查看sys模块的可用方法(很多),下面列举几种。
sys.path包含输入模块的目录名列表:

>>> sys.path
['',
 'D:\\python3.6.6\\Lib\\idlelib', 
'D:\\python3.6.6\\python36.zip', 
'D:\\python3.6.6\\DLLs', 
'D:\\python3.6.6\\lib', 
'D:\\python3.6.6', 
'D:\\python3.6.6\\lib\\site-packages']

该命令获取了指定模块搜索路径的字符串集合。将写好的模块放在上面得到的某个路径下,就可以在使用import导入时正确找到,也可以用sys.path.append(自定义路径)添加模块路径。——“自定义模块乱放程序是找不到的!”
sys.argv在外部向程序内部传递参数:
????
sys.argv变量是一个包含了命令行参数的字符串列表,利用命令行向程序传递参数。其中脚本的名称是sys.argv列表的第一个参数。

④数学:math模块
math模块也是python自带模块,包含了和数学运算公式相关的信息——P125

⑤随机数:random模块
列举常用模块:
生成随机整数(需指定上下限,且下限小于上限):randint

import random
>>> random.randint(10,2390)  #生成指定范围内的随机整数
2375

生成随机浮点数:random

>>> random.random()
0.9935870033845187
>>> random.uniform(10,100)
27.07308173076904
>>> random.uniform(100,10)
18.198994262912336

随机字符:choice

>>> random.choice('98%$333#@')
'3'

洗牌:shuffle

>>> test = ['a','B',1,2,5,'%']
>>> random.shuffle(test)
>>> print(test)
[5, 1, 2, 'a', 'B', '%']

三、类与对象

1、面向对象概述

面向对象编程(Object Oriented Programming,OOP),是一种程序设计思想,是以建立模型体现出来的抽象思维过程和面向对象的方法。
模型是用来反映现实世界中的事物特征的,是对事物特征和变化规律的抽象化,是更普遍、更集中、更深刻地描述客体的特征。
OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

术语简介:

1、类:是创建对象的代码段,描述了对象的特征、属性、要实现的功能,以及采用的方法等。
2、属性:描述了对象的静态特征。
3、方法:描述了对象的动态动作。
4、对象:对象是类的一个实例,就是模拟真实事件,把数据和代码段都集合到一起,即属性、方法的集合。
5、实例:就是类的实体。
6、实例化:创建类的一个实例过程。
7、封装:把对象的属性、方法、事件集中到一个统一的类中,并对调用者屏蔽其中的细节。
8、继承:一个类共享另一个类的数据结构和方法的机制称为继承。起始类称为基类、超类、父类,而继承类称为派生类、子类。继承类是对被继承类的拓展。
9、多态:一个同样的函数对于不同的对象可以具有不同的实现。
10、接口:定义了方法、属性的结构,为其成员提供违约,不提供实现。不能直接从接口创建对象,必须首先创建一个类来实现接口所定义的内容。
11、重载:一个方法可以具有许多不同的接口,但方法的名称是相同的。
12、事件:事件是由某个外部行为所引发的对象方法。
13、重写:在派生类中,对基类某个方法的程序代码及进行重新编码,使其实现不同的功能。
14、构造函数:是创建对象所调用的特殊方法。
15、析构函数:是释放对象时所调用的特殊方法。

2、类

①类的定义
类就是对象的属性和方法的拼接,静态的特征称为属性,动态的动作称为方法。

>>> class Person:  #规定类名以大写字母开头
    #属性
    skincolor = "yellow"
    high = 185
    weight = 75
    #方法
    def goroad(self):
        print("人走路动作的测试...")
    def sleep(self):
        print("睡觉,晚安!")

②类的使用(类实例化为对象)

>>> p = Person()  #将类实例化为对象,注意后面要加括号()

③类的构造方法及专有方法
类的构造方法:__int__(self)。只要实例化一个对象,此方法就会在对象被创建时自动调用——实例化对象时是可以传入参数的,这些参数会自动传入__int__(self,param1,param2...)方法中,可以通过重写这个方法来自定义对象的初始化操作。

>>> class Bear:
    def __init__(self,name):
        self.name = name
    def kill(self):
        print("%s是保护动物不可猎杀"%self.name)

        
>>> a = Bear("狗熊")
>>> a.kill()
狗熊是保护动物不可猎杀

解释:与Person()相比,这里重写了__init__()方法,不然默认为__init__(self)。在Bear()中给了一个参数name,成了__init__(self,name),第一个参数self是默认的,所以调用时把“狗熊”传给了name。也可以给name默认参数,这样即使忘记传入参数,程序也不会报错:

>>> class Bear:
    def __init__(self,name = "狗熊"):
        self.name = name
    def kill(self):
        print("%s是保护动物不可猎杀"%self.name)
    
>>> b = Bear()
>>> b.kill()
狗熊是保护动物不可猎杀
>>> c = Bear("丹顶鹤")
>>> c.kill()
丹顶鹤是保护动物不可猎杀

④类的访问权限
在C++和JAVA中是通过关键字public、private来表明访问权限是共有的还是私有的。在python中,默认情况下对象的属性和方法是公开的、公有的,通过点(.)操作符来访问。如上面的kill()函数(方法)的访问,也可以访问变量:

>>> class Test:
    name = "大连橡塑"

    
>>> a = Test()
>>> a.name
'大连橡塑'

若变量前面加上双下划线(__)就表示声明为私有变量就不可访问:

>>> class Test:
    __name = "君欣旅店"
    
>>> a = Test()
>>> a.__name
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    a.__name
AttributeError: 'Test' object has no attribute '__name'

可利用函数方法访问私有变量:

>>> class Test:
    __name = "君欣旅店"
    def getname(self):
        return self.__name

>>> a = Test()
>>> a.getname()
'君欣旅店'

也可通过"_类名__变量名"格式访问私有变量:

>>> a._Test__name
'君欣旅店'

(由此可见python的私有机制是伪私有,python的类是没有权限控制的,变量可以被外界调用)

⑤获取对象信息
类实例化对象后(如a = Test()),对象就可以调用类的属性和方法:
即a.getname()、a.name、a.kill()这些

3、类的特点

①封装
形式上看,对象封装了属性就是变量,而方法和函数是独立性很强的模块,封装就是一种信息掩蔽技术,使数据更加安全!
如列表实质上是python的一个序列对象,sort()就是其中一个方法/函数:

>>> list1 = ['E','C','B','A','D']
>>> list1.sort()
>>> list1
['A', 'B', 'C', 'D', 'E']

②多态
不同对象对同一方法响应不同的行动就是多态(内部方法/函数名相同,但是不同类里面定义的功能不同):

>>> class Test1:
    def func(self):
        print("这是响应1...")        
>>> class Test2:
    def func(self):
        print("这是响应2...")    
>>> x = Test1()
>>> y = Test2()
>>> x.func()
这是响应1...
>>> y.func()
这是响应2...

注意:self相当于C++的this指针。由同一个类可以生成无数个对象,这些对象都源于同一个类的属性和方法,当一个对象的方法被调用时,对象会将自身作为第一个参数传给self参数,接收self参数时,python就知道是哪个对象在调用方法了。

③继承
继承是子类自动共享父类数据和方法的机制。语法格式如下:

class ClassName(BaseClassName):
...
ClassName:子类名称,第一个字母必须大写。
BaseClassName/paraname:父类名称。

子类可继承父类的任何属性和方法,如下定义类Test_list继承列表list的属性和方法(append和sort):

>>> class Test_list(list):
    pass

>>> list1 = Test_list()
>>> list1.append('B')
>>> list1
['B']
>>> list1.append('UCT')
>>> list1
['B', 'UCT']
>>> list1.sort()
>>> list1
['B', 'UCT']

使用类继承机制时的注意事项:
a、若子类中定义与父类同名的方法或属性,自动覆盖父类里对应的属性或方法。
b、子类重写父类中同名的属性或方法,若被重写的子类同名的方法里面没有引入父类同名的方法,实例化对象调用父类的同名方法就会出错:

import random
class Dog:
    def __init__(self):
            self.x = random.randint(1,100)  #两个缩进
            self.y = random.randint(1,100)
    def run_Dog(self):
            self.x += 1
            print("狗狗的位置是:",self.x,self.y)

        
class Dog1(Dog):
    pass

class Dog2(Dog):
    def __init__(self):
            self.hungry = True  
    def eat(self):
            if self.hungry:
                print("狗想吃东西了!")
                self.hungry = False
            else:
                print("狗吃饱了!")

调用:
>>> dog = Dog()
>>> dog.run_Dog()
狗狗的位置是: 62 69
>>> dog1 = Dog1()
>>> dog1.run_Dog()
狗狗的位置是: 29 89
>>> dog2 = Dog2()
>>> dog2.eat()
狗想吃东西了!
>>> dog2.eat()
狗吃饱了!
>>> dog2.run_Dog()  #报错报错!!!

分析:子类Dog2重写了父类(基类)Dog的构造函数__init__(self),则父类构造函数里的方法被覆盖。要解决此问题,就要在子类里面重写父类同名方法时,先引入父类的同名方法,两种技术:a、调用未绑定的父类方法;b、使用super函数

a、调用未绑定的父类方法——语法格式:paraname.func(self)。父类名.方法名.(self)
对上面示例代码的Dog2类更改:

def __init__(self):
        Dog.__init__(self)   #加了这一行
            self.hungry = True  
调用:
>>> dog2 = Dog2()
>>> dog2.run_Dog()
狗狗的位置是: 85 16

b、使用super函数,该函数可以自动找到父类方法和传入的self参数,语法格式:super().func([parameter])。parameter为可选参数,若是self可省略。
对上面示例代码的Dog2类更改:

def __init__(self):
            super().__init__()  #加了这一行
            self.hungry = True

调用:
>>> dog2 = Dog2()
>>> dog2.run_Dog()
狗狗的位置是: 96 82

使用super函数的方便之处在于不用写任何关于基类(父类)的名称,直接写重写的方法即可,会自动去父类去寻找,尤其在多重继承中,或者子类有多个祖先类时,能自动跳过多种层级去寻找。如果以后要更改父类,直接修改括号()里面的父类名称即可,不用再修改重写的同名方法里的内容。

④多重继承
一个子类同时继承多个父类的属性和方法:

class Classname(Base1,Base2,Base3):
    ...

虽然多重继承的机制可以使子类继承多个属性和方法,但是容易导致代码混乱,引起不可预见的Bug,一般尽量避免使用。

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯宝最帅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值