python函数基础


一、函数

1.python中函数的定义

所谓函数,就是把 具有独立功能的代码块 组织为一个小模块,在需要的时候 调用
函数的调用包括两个步骤: 
1.定义函数一一封装 独立的功能
2.调用函数一一享受 封装 的成果
函数的作用,在开发程序时,使用函数可以提高编写的效率以及代码的 重用

2.如何定义函数

1.def 是英文 define(定义) 的缩写
2.函数名称 应该能够表达 函数封装代码 的功能,方便后续的调用
3.函数名称 的命名应该 符合 标识符的命名规则
可以由字母、下划线和数字组成
不能以数字开头
不能与关键字重名

在这里插入图片描述

3.函数的参数

开发一个 sum_2_num 的函数
函数能够实现 两个数字的求和 功能

代码如下:

def sum_2_num():
    num1 = 10
    num2 = 20
    result = num1 + num2
    print("%d + %d = %d" % (num1, num2, num1 + num2))
sum_2_num()

在函数名的后面小括号内部填写 参数
多个参数之间使用 , 分离

def sum_2_num(num1,num2):
    result = num1 + num2
    print("%d + %d = %d" % (num1, num2, num1 + num2))
sum_2_num(50,20)

4.参数的作用

函数,把具有独立功能的代码块组织为一个小模块,在需要的时候调用函数的参数、增加函数的通用性,针对 相同的数据处理逻辑、能够适应 更多的数据

1.在函数内部,把参数当做变量使用,进行需要的数据处理
2.函数调用时,按照函数定义的参数顺序、把希望在函数内部处理的数据 通过 参数 传递
形参:定义函数时,小括号中的参数,是用来接收参数用的,在函数内部作为变量使用
实参:调用函数时,小括号中的参数,是用来把数据传递到函数内部用的

5.函数的嵌套调用

一个函数里面又 调用了 另外一个函致,这就是 函数嵌套调用
如果函数 test2 中,调用了另外一个函数 test1 
那么执行到调用 test1 函数时,会先把函数test1中的任务都执行完
才会回到 test2 中调用函数 test1 的位置,继续执行后续的代码
def test1():
     print("*" * 50)
     print("test 1")
     print("*" * 50)

def test2():
     print("_" * 50)
     print("test 2")

     test1()
     print("_" * 50)
test2()

6.函数的递归

 函数调用自身的编程技巧称为递归

递归函数的特点:

  • 一个函数 内部 调用自己
  • 函数内部可以调用其他函数,当然在函数内部也可以调用自己

代码特点:

  • 函数内部的 代码 是相同的,只是针对 参数 不同,处理的结果不同
  • 当参数满足一个条件时,函数不再执行
    这个非常重要,通常被称为递归的出口,否则会出现死循环

7.内部函数

在函数内部定义另一个函数,也就是函数的嵌套
在外部函数的作用域内,外部函数可以随意调用内部函数
由于内部函数的整个定义过程都在外部函数中,所以出了外部函数就无法再被调用了

def outside():
    print('外部函数被调用')
    def inside():
         print('内部函数被调用')
    inside()


执行结果
>>>outside() 
外部函数被调用 
内部函数被调用
>>>inside() NameError: name 'inside' is not defined 

内部函数的特点:
1.可以访问外部函数的变量
2.内部函数可以修改外部函数的可变类型的变量 比如:list1
3.函数内部修改全局变量,要加global 变量名,内部函数要使用外部函数的不可变类型的变量的时候要加nonlocal
4.locals() 查看本地变量有哪些,以字典的形式输出
globals() 查看全局变量有哪些,以字典的形式输出()

内部函数案例:

  • 使用locals() 内置函数进行查看,可以看到在当前函数中声明的内容有哪些
  • locals() 返回一个字典。 以 key:values的形式存在
a = 100  # 全局变量

def func():
     b = 99  
     # 内部函数   
     def inner_func():        
            nonlocal b        
            global a        
            c = 88   
            # 尝试修改   
            b += 1      
            a += 10
            c += 12
            # 尝试打印
            print(a, b, c)
     inner_func()
     print(locals())
     print(globals())
     
func()

8.闭包

如果在一个内部函数里引用了外部函数的变量,此时这个内部函数就被称为闭包

#格式:

def 外部函数():
   ...  
   def 内部函数()
        ...
   return 内部函数

二、集合

1.集合的操作

集合的定义:
某些指定的对象集在一起就成为一个集合,其中每一个对象叫元素
格式:集合名 = {元素1,元素2,…}
例:my_set = {1, 3, 5}

集合是无序的-> 不支持下标索引
集合是可变的数据类型
集合中的元素是唯一的
集合一般用于元组或者列表中的元素去

定义一个空的集合 my_set = set()
注意: my_set = {} 这样的写法为一个空字典

集合的常见操作:
.add() #增加元素

s1 = set()
s1.add('hello')
s1.add('小猪佩奇')
s1.add('lucy')
print(s1)

.update() #可以添加多个元素

t1 = (‘林志玲’, ‘言承旭’)
s1.update(t1)

print(s1)

s1.add(t1)  # t1作为一个元素被添加进集合
print(s1)

.remove() #增加删除元素,如果元素存在则删除,不存在则报错:KeyError:

s1.remove("言承旭")

print(s1)

s1.remove('道明寺')
print(s1)

.pop() #随机删除

s1.pop()
print(s1)

.discard() # 类似remove() 移除不存在的元素的时候会报错

s1.discard("道明寺")
print(s1)

.clear() #清空集合

s1.clear()
print(s1)

2.交集和并集

集合之差集(-)
.difference()

set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
new_set = set1 - set2
new_set1 = set2 - set1
print(new_set)
print(new_set1)


new_set = set1.difference(set2)
new_set1 = set2.difference(set1)
print(new_set)
print(new_set1)

集合之交集( & )
.intersection()

set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
new_set = set1 & set2
print(new_set)


set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}

# new_set = set1.intersection(set2)
new_set = set2.intersection(set1)
print(new_set)

集合之并集( | )
.union()

set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
new_set = set1 | set2
print(new_set)


set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
new_set = set1.union(set2)
print(new_set)

集合之对称差集(^
在前一个集合或后一个集合中但不会同时出现在二者中
.symmetric_difference()

set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
#new_set = (set1 | set2) - (set1 & set2)
new_set = set1 ^ set2
print(new_set)


# new_set = set1.symmetric_difference(set2)
new_set = set2.symmetric_difference(set1)
print(new_set)

3.可变和不可变类型转换

不可变类型,内存中的数据不允许被修改:
数字类型 int bool float,complex,long(2.x)
字符串 str
元组 tuple

可变类型,内存中的数据可以被修改
列表 list
字典 dict
集合/frozenset()除外,frozenset() 返回一个冻结的集合,冻结后集合不能再添加或删除任何元素

案例演示:
列表

a = [1, 2, 3]
print(id(a))

a.append(999)
print(a)
print(id(a))

a.remove(2)
print(a)
print(id(a))

a.clear()
print(a)
print(id(a))

字典

d = {"name": "xiaoming"}
print(id(d))

d["age"] = 20
print(d)
print(id(d))

d.pop("age")
print(d)
print(id(d))

d.clear()
print(d)
print(id(d))

注意:
可变类型的数据变化,是通过方法来实现的
如果给一个可变类型的变量,赋值了一个新的数据,引用会被修改
变量不再对之前的数据引用
变量改为对新赋值的数据引用

字典的 key 只能使用不可变类型的数据

d = {"name": "小明"}
d[1] = "整数"
d[(2,)] = "元组"
print(d)

运行结果:
{'name':'小明',1:'整数'(2):'元组'}

数据类型转换
str() - int() list() set() tuple()

str() - int() list() set() tuple()

list() - set() tuple() dict() # 转换成字典,元素要有固定的格式

dict() - list() # 只能保存字典的键

三、面向对象基础

1.面对对象

面对对象(OOP)基本概念
把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。对同类对象抽象出其共性,形成类。
面向过程和面向对象,是两种不同的编码方式
过程和函数

  • 过程是早期的一个编程概念
  • 过程类似于函数,只能执行,但是没有返回值
  • 函数不仅能执行,还可以返回结果

面向过程–怎么做

  • 把完成某一个需求的 所有步骤 从头到尾 逐步实现
  • 根据开发需求,将某些 功能独立 的代码 封装 成一个有一个 函数
  • 最后完成的代码,就顺序地调用不同的函数

特点

  • 注重步骤与过程,不注重职责分工
  • 如果需求复杂,代码会变得很复杂
  • 开发复杂项目,没有固定的套路,开发难度很大

面向对象–谁来做

  • 在完成某一个需求前,首先 确定职责一一要做的事情(方法)
  • 根据 职责 确定不同的 对象,在 对象 内部封装不同的方法(多个)
  • 最后完成的代码,就顺序地让 不同的对象 调用 不同的方法

特点

  • 注重对象和职责,不同的对象承担不同的职责
  • 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
  • 需要在面向过程基础上,再学习一些面向对象的语法

类和对象 是 面向对象编程的两个核心概念
类是对一群具有相同特征或者行为 的事物的一个统称,是抽象的,不能直接使用
特征 被称为属性
行为 被称为方法

对象

 对象 是 由类创建出来的一个具体存在,可以直接使用
由哪一个类创建出来的 对象,就拥有在哪一个类 中定义的:属性、方法
对象就是相当于用图纸制造的飞机
在程序开发中,应该先有类,再有对象

类和对象的关系

  • 类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象
  • 类只有一个,而对象可以有很多个
    不同的对象之间属性 可能会各不相同
  • 类中定义了什么属性和方法,对象中就有什么属性和方法,不可能多,也不可能少

类的设计
在程序开发中,要设计一个类,通常需要满足三个要素:
1.类名 这类事物的名字,满足大驼峰命名法
2.属性 这类事物具有什么样的特征
3.方法 这类事物具有什么样的行为
大驼峰命名法
CapWords
1.每一个单词的首字母大写
2.单词与单词之间没有 下划线

2.定义简单化

定义简单的类(只包含方法)

  面向对象是更大的封装, 在一个类中封装多个方法, 这样通过这个类创建出来的对象,就可以直接调用这些方法了!

定义只包含方法的类:
在这里插入图片描述
在这里插入图片描述
案例演示
需求:小猫爱吃鱼

class Cat:
      """这是一个猫类"""
      def eat(self):
          print("小猫爱吃鱼")
      def drink(self):
          print("小猫在喝水")

# 创建猫对象
tom = Cat()
tom.eat()
tom.drink()
print(tom)


结果输出
小猫爱吃鱼
小猫在喝水
<__mian__.Cat.object at 0x000001ccc70AE1D0>

3.初始化

初始化方法

当使用类名( ) 创建对象时,会自动执行以下操作
1.为对象在内存中分配空间一一创建对象---  __new__
2.为对象的属性设置初始值一一初始化方法(__init__)

这个 初始化方法 就是__init__方法, __init__是对象的内置方法
__init__方法是 专门 用来定义ー个类 具有 哪些属性的方法!

在初始化方法内部定义属性

在__init__方法内部使用 self.属性名=属性的初始值 就可以 定义属性
定义属性之后,再使用 Cat 类创建的对象,都会拥有该属性
class Cat:
     """这是一个猫类"""
     def __init__(self):
         print("初始化方法")
         # 定义用 Cat 类创建的猫对象都有一个 name 的属性  
          self.name = "Tom"
          
     def eat(self):    
         print("%s爱吃鱼" % self.name)
# 使用类名() 创建对象的时候,会自动调用初始化方法 __init__
tom = Cat()
tom.eat()

四、私有属性单继承和重写

1.私有属性

私有属性和方法

在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部被访问到
私有属性就是对象不希望公开的属性
私有方法就是对象不希望公开的方法

定义方式

在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法

私有属性在外界不能被直接访问

class Girl:
    def __init__(self, name):
          self.name = name
          self.__age = 18  # 将 age 设置成私有属性  
        
    def secret(self):
          print("{}的年龄是{}岁".format(self.name, self.__age))

Anna = Girl("小美")
Anna.secret()
print(Anna)

# 私有属性,不能被外界访问!
print(Anna.__age) 


程序运行结果:
AttributeError: 'Girl' object has no attribute '__age'

私有方法在外界也不能直接调用

2.单继承

面对对象三大特性
1.封装根据职责将属性和方法封装到一个抽象的 类中
2.继承实现代码的重用,相同的代码不需要重复的编写
3.多态不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

 继承的概念:子类拥有父类的所有方法和属性

继承的语法:

 class 子类名(父类名):
       pass

子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
子类中应该根据职责,封装子类特有的属性和方法

继承的传递性
C类从B类继承,B类又从A 类继承
那么C类就具有B类和A 类的所有属性和方法
子类拥有父类以及父类的父类中封装的所有属性 和方法

3.方法的重写

子类拥有父类的所有方法和属性
子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
当父类的方法实现不能满足子类需求时,可以对方法进行重写(override)

重写 父类方法有两种情况
- 覆盖父类的方法
- 对父类方法进行扩展

覆盖父类的方法:

  • 如果在开发中,父类的方法实现和子类的方法实现 完全不同
  • 就可以使用覆盖的方式,在子类中重新编写父类的方法实现
    具体的实现方式,就相当于在子类中定义了一个 和父类相同名的方法并且实现

重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法

对父类方法进行扩展

如果在开发中,子类的方法实现中包含父类的方法实现
    父类原本封装的方法实现是子类方法的一部分
就可以使用扩展的方式
    在子类中重写父类的方法
    在需要的位置使用 super( ).父类方法 来调用父类方法的执行
    代码其他的位置针对子类的需求,编写子类特有的代码实现

关于 super
    在python 中 super 是一个 特殊的类
    super( )就是使用 super 类创建出来的对象
    最常使用的场景就是在重写父类的方法时,调用 在父类中封装的方法实现

五、异常获取

1.异常

异常的概念

程序在运行时,如果Python解释器遇到一个错误,会停止程序的执行,并且提示一些错误信息。这就是异常 
程序停止执行并且提示错误信息这个动作,我们通常称之为:抛出(raise)异常

异常捕获 – 简单的捕获异常语法
程序在运行时,如果对某些代码的执行不能确定是否正确,可以增加try来捕获异常
捕获异常最简单的语法格式:
在这里插入图片描述
错误类型捕获
在程序执行时,可能会遇到不同类型的异常,并且需要针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
语法如下:
在这里插入图片描述
当 Python 解释器 抛出异常 时,最后一行错误信息的第一个单词,就是错误类型

捕获错误未知
在开发时,要预判到所有可能出现的错误,还是有一定难度的
如果希望程序无论出现任何错误,都不会因为 Python 解释器抛出异常而被终止,可以增加一个except
语法如下:

except  Exception as result:
      print("未知错误%s" % result)

异常捕获完整语法

在这里插入图片描述
异常的传递

  • 异常的传递 — 当函数/方法执行出现异常,会将异常传递给函数/方法的调用一方
  • 如果传递到主程序,仍然没有异常处理,程序才会被终止
    提示:
  • 在开发中,可以在主函数中增加 异常捕获
  • 而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的异常捕获中
  • 这样就不需要在代码中,增加大量的异常捕获,能够保证代码的整洁

六、多继承和多态

1.多继承

多继承:子类可以拥有多个父类,并且具有所有父类的属性和方法

class Base1:
     def demo1(self):
         print("Base1 的方法")
        
class Bass2:
     def demo2(self):
         print("Base2 的方法")
         
class Derived(Base1, Bass2):
     def test(self):
         self.demo1()    
         self.demo2()
         
test = Derived()
test.demo1()
test.demo2()

开发时,应该尽量避免这种容易产生混淆的情况!——如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承

2.多态

1.封装根据职责将属性和方法封装到一个抽象的 类 中
      定义类的准则
2.继承 实现代码的重用,相同的代码不需要重复的编写
      设计类的技巧
      子类针对自己特有的需求,编写特定的代码
3.多态不同的子类对象,调用相同的父类方法,产生    不同的执行结果
      多态可以增加代码的灵活度
      以继承和重写父类方法为前提
      是调用方法的技巧,不会影响到类的内部设计

七、类属性类方法静态方法单例模式

1.类属性

类的结构
术语–实例

  • 使用面向对象开发,第1步是设计类
  • 使用类名( ) 创建对象, 创建对象的动作有两步:
    在内存中为对象分配空间
    调用初始化方法 init 为对象初始化
  • 创建对象后,内存中就有了一个对象的实实在在的存在 — 实例

通常也会把:

1.创建出来的对象叫做类的实例
2.创建对象的动作叫做实例化
3.对象的属性叫做实例属性
4.对象调用的方法叫做实例方法

在程序执行时

对象各自拥有自己的实例属性
调用对象方法,可以通过 self
    访问自己的属性
    调用自己的方法

结论

每一个对象,都有自己独立的内存空间,保存各自不同的属性
对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用,传递到方法内部

类属性和实例属性
类属性就是给类对象中定义的属性
通常用来记录与这个类相关的特征
类属性不会用于记录具体对象的特征

在这里插入图片描述
如果使用 对象.类属性 = 值 赋值语句,只会给对象添加一个属性,而不会影响到类属性的值

2.类方法和静态方法

  • 类属性 就是针对 类对象 定义的属性
    使用赋值语句在 class 关键字下方可以定义类属性
    类属性用于记录与这个类相关的特征
  • 类方法 就是记录与这个类相关的方法
    在类方法内部可以直接访问类属性或者调用其他的类方法

语法如下
在这里插入图片描述

类方法需要用修饰器 @classmethod 来标识,告诉解释器这是一个类方法
类方法的第一个参数应该是 cls
    由哪一个类调用的方法,方法内的cls就是哪一个类的引用
    这个参数和实例方法的第一个参数和self 类似
    提示:使用其他名称也可以,不过习惯使用 cls
通过类名,调用类方法,调用方法时,不需要传递 cls 参数
在方法内部
     可以通过cls访问类的属性
     也可以通过cls 调用其他的类方法

静态方法

  • 在开发时,如果需要在类中封装一个方法,这个方法:
    既不需要访问实例属性或者调用实例方法
    也不需要访问类属性或者调用类方法
  • 这个时候,可以把这个方法封装成一个静态方法

语法如下:
在这里插入图片描述

3.单例模式

单例 设计模式

  • 设计模式
    设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计都是针对某一特定问题的成熟的方案
    使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性

  • 单例设计模式
    目的 — 让类创建的对象,在系统中只有唯一的一个实例
    每一次执行类名( ) 返回的对象,内存地址是相同的

  • 单例设计模式的应用场景
    音乐播放 对象
    回收站 对象
    打印机 对象
    ……

new 方法

  • 使用类名( )创建对象时,Python的解释器首先 会 调用 new 方法为对象分配空间
  • new 是一个由object 基类提供的内置的静态方法,是要作用有两个:
    在内存中为对象分配空间
    返回对象的引用
  • Python 的解释器获得对象的引用后,将引用作为第一个参数,传递给 __init__方法

重写 new 方法的代码非常固定!
重写 new 方法 一定要return super( ).new(cls)
否则Python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
注意new 是一个静态方法,在调用时需要主动传递 cls 参数
在这里插入图片描述
Python 中的单例

  • 单例 — 让类创建的对象,在系统中只有唯一的一个实例
    定义一个类属性,初始值是 None ,用于记录单例对象的引用
    重写 new 方法
    如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
    返回类属性中记录的对象引用

案例

class MusicPlayer(object)
      pass
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)


程序运行结果
<__main__.MusicPlayer object at 0x00A0E6B8>
<__main__.MusicPlayer object at 0x00A0EC10>

只执行一次初始化工作

  • 在每次使用类名( ) 创建对象时,Python的解释器都会自动调用两个方法:
    new 分配空间
    init 对象初始化
  • 在对 new 方法改造后,每次都会得到第一次被创建对象的引用
  • 但是:初始化方法还会被再次调用
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值