IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> python基础知识整理二:类和函数 -> 正文阅读

[C++知识库]python基础知识整理二:类和函数

1、类

1、面向对象编程

2分钟让你明白什么是面向对象编程

面向对象与面向过程的本质的区别

面向对象编程(object oriented programming,OOP):

  • 对象 + 类 + 继承 + 多态 + 消息

  • 现实世界存在的任何事务都可以称之为对象,对象一般由属性+方法组成。

  • 类:用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

  • 当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。

例子:

问题: 洗衣机里面放有脏衣服,怎么洗干净?

面向过程的解决方法:

  1. 执行加洗衣粉方法;
  2. 执行加水方法;
  3. 执行洗衣服方法;
  4. 执行清洗方法;
  5. 执行烘干方法;

面向对象的解决方法:

  1. 我先弄出两个对象:“洗衣机”对象和“人”对象
  2. 针对对象“洗衣机”加入一些属性和方法:“洗衣服方法”“清洗方法”、“烘干方法”
  3. 针对对象“人”加入属性和方法:“加洗衣粉方法”、“加水方法”
  4. 然后执行:
    人.加洗衣粉; 人.加水
    洗衣机.洗衣服; 洗衣机.清洗; 洗衣机.烘干

解决同一个问题 ,

面向过程编程就是将解决这个问题的过程拆成一个个方法(是没有对象去调用的),通过一个个方法的执行来解决问题。

面向对象编程就是先抽象出对象,然后用对象执行方法的方式解决问题。

1.1 面向过程和面向对象的比较

面向过程:

  • 面向过程就是以过程为中心,分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

  • 优点:
    性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;
    比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

  • 缺点:没有面向对象易维护、易复用、易扩展(耦合性高,导致可维护性比较差)

面向对象:

  • 面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

  • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

  • 可维护性主要表现在3个方面:可理解性、可测试性和可修改性。面向对象的好处之一就是显著的改善了软件系统的可维护性。

  • 缺点:性能比面向过程低

1.2 面向对象编程的特性

三大基本特性:封装,继承,多态

封装

Python封装机制及实现方法

  • 封装,就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

  • 一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。

  • 在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。

  • 封装机制保证了类内部数据结构的完整性,因为使用类的用户无法直接看到类中的数据结构,只能使用类允许公开的数据,很好地避免了外部对内部数据的影响,提高了程序的可维护性。

  • 对一个类实现良好的封装,用户只能借助暴露出来的类方法来访问数据,我们只需要在这些暴露的方法中加入适当的控制逻辑,即可轻松实现用户对类中属性或方法的不合理操作。

  • 对类进行良好的封装,还可以提高代码的复用性。

继承

  • 继承,指可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。

  • 继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

  • 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。

  • 要实现继承,可以通过 “继承”(Inheritance)和“组合”(Composition)来实现。

  • 继承概念的实现方式有二类:实现继承与接口继承。
    实现继承是指直接使用父类的属性和方法而无需额外编码的能力;
    接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。

多态

  • 多态,是指一个类实例的相同方法在不同情形有不同表现形式。

  • 多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。

2、类的基本概念

数据成员:

  • 类变量或者实例变量,用于处理类及其实例对象的相关的数据。

类变量:

  • 类变量在整个实例化的对象中是公用的。所有实例共享一个属性

  • 类变量定义在类中且在函数体之外

  • 类变量通常不作为实例变量使用。

  • 类方法的调用方式有 2 种,
    既可以使用类名直接调用,也可以使用类的实例化对象调用。

  • 可以通过类名修改类变量,会作用到所有的实例对象。

  • 可以通过类对象可以访问类变量,但无法修改类变量的值。这是因为,通过类对象修改类变量的值,不是在给“类变量赋值”,而是定义新的实例变量。

  • 可以动态的为类和对象添加类变量

实例变量:

  • 在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,

  • 在类声明的内部但是在类的其他成员方法之外声明的

  • 不可以对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,此时访问到的是实例属性
    当删除该实例属性后,再使用相同的名称,访问到的将是类属性。

  • 实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。

Python类变量和实例变量(类属性和实例属性)

class CLanguage :
    # 下面定义了2个类变量
    name = "C语言中文网"
    add = "http://c.biancheng.net"
    # 下面定义了一个say实例方法
    def say(self, content):
        print(content)

# 使用类名直接调用
print(CLanguage.name)
print(CLanguage.add)

# 使用类对象调用
clang1 = CLanguage()
print(clang1.name)
print(clang1.add)

# 使用类名修改类变量的值
CLanguage.name = "Python教程"
CLanguage.add = "http://c.biancheng.net/python"
print(CLanguage.name)
print(CLanguage.add)

# 动态地为类和对象添加类变量
clang2 = CLanguage()
CLanguage.catalog = 13
print(clang2.catalog)

局部变量:

  • 定义在方法中的变量,只作用于当前实例的类。

实例化:

  • 创建一个类的实例,类的具体对象。
  • 创建类对象的过程,又称为类的实例化。
  • 定义的类只有进行实例化,也就是使用该类创建对象之后,才能得到利用。

方法:

  • 类中定义的函数。

对象:

  • 通过类定义的数据结构实例。
  • 对象包括两个数据成员(类变量和实例变量)和方法。

访问类的属性:

  • hasattr(obj, name):检查是否存在一个属性。

  • getattr(obj, name[, default]) ;访问对象的属性。

  • setattr(obj, name, value) :设置一个属性。如果属性不存在,会创建一个新属性。

  • delattr(obj, name) :删除属性。


 '''
__init__是类的构造函数或者初始化方法,当创建了这个类的实例就会调用该方法

self代表类的实例,代表当前对象的地址

self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。

self 不是 python 关键字,可以换成别的也可以(一般习惯用self)

empCount是 类变量
 '''
 
class Employee:
   '所有员工的基类'
   empCount = 0      # 类变量
 
   def __init__(self, name, salary):    
      self.name = name     
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount

emp1 = Employee('bob', 100)
hasattr(emp1, 'age')    # 如果存在 'age' 属性返回 True。
getattr(emp1, 'age')    # 返回 'age' 属性的值
setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8
delattr(emp1, 'age')    # 删除属性 'age'

3、继承与多态

python 多继承与继承原理及多继承中super本质

Python 类的继承和多态

python类的继承

Python 面向对象

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。

什么时候使用继承

  • 假如我需要定义几个类,而类与类之间有一些公共的属性和方法,这时我就可以把相同的属性和方法作为基类的成员,而特殊的方法及属性则在本类中定义。这样子类只需要继承基类(父类),子类就可以访问到基类(父类)的属性和方法了,它提高了代码的可扩展性和重用性。

判断继承:

  • isinstance(obj, Class) :如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

  • issubclass(sub,sup) :用于检查类继承。判断一个类是另一个类的子类或者子孙类

子类继承父类构造函数:

  1. :子类可以没有构造函数,表示同父类构造一致:
    子类不重写__init__()方法,实例化子类后,会自动调用父类的__init__()的方法。

  2. 子类重写__init__()方法,实例化子类后,将不会自动调用父类的__init__()的方法。

  3. 子类重写__init__()方法又需要调用父类的构造函数的方法:
    子类既继承了父类一部分或者全部的属性,自己又新增一部分属性。(先继承再构造)
    在这里插入图片描述
    在这里插入图片描述

  • 可以将实例用作属性添加到另一个类中去, 则每次调用__init__方法的时候都将执行该操作

多继承

  • 一个类继承自多个类就是多继承,它将具有多个类的特征。

  • 如果子类和父类有相同的方法,就会调用子类中的方法。

  • 如果不同的父类中存在着相同的方法名称,多个父类会根据它们在列表中的顺序被检查,如果对下一个类存在两个合法的选择,选择第一个父类

多态:鸭子类型

  • 子类可以对方法重写

  • 不同的子类对象调用相同的父类方法,产生不同的执行结果

  • 多态可以增加代码的灵活度


# 父类
class Person(object):
 
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.weight = 'weight'
 
    def talk(self):
        print("person is talking....")

class Book():
	pass

 
# 直接继承父类的构造函数,实例化后自动调用person的__init__方法
# 子类新增方法listen
class Janpanese(Person):   
 
    def listen(self):
        print('is listening...')


# 重写__init__方法,并继承父类的构造方法 
# 子类重构方法talk
class Chinese(Person):
 
    def __init__(self, name, age, language): 
        Person.__init__(self, name, age) 
        self.language = language  # 定义类的本身属性
       # 创建一个新的Book实例,并将其存储在属性self.book中,
       # 每次调用__init__方法的时候都将执行该操作
        self.book = Book()   
       
 # 子类重构talk方法
 	def talk(self):  
        print('%s is speaking chinese' % self.name)
 
# 多继承
# 重写__init__方法,并继承父类的构造方法 
# 新增方法height
class American(Person, Chinese):
 
    def __init__(self, name, age, language): 
    	super(Person,self).__init__(name,age,)  
    	super(Chinese,self).__init__(language)  
        self.language = language
      
 
    def height(self):
        print('is tall')

one = Person('w', 10)
two = American('w', 10, 'English')

print(isinstance(two, Person))        # True
print(isinstance(two, Person))        # True
print(isinstance(two, American))      # True
print(isinstance(American, Person))   # True
 
鸭子模型:
class WhoSay:
    def say(self,who):
        who.say()
class CLanguage:
    def say(self):
        print("调用的是 Clanguage 类的say方法")

class CPython(CLanguage):
    def say(self):
        print("调用的是 CPython 类的say方法")

class CLinux(CLanguage):
    def say(self):
        print("调用的是 CLinux 类的say方法")
a = WhoSay()
#调用 CLanguage 类的 say() 方法
a.say(CLanguage())
#调用 CPython 类的 say() 方法
a.say(CPython())
#调用 CLinux 类的 say() 方法
a.say(CLinux())

输出结果:

调用的是 Clanguage 类的say方法
调用的是 CPython 类的say方法
调用的是 CLinux 类的say方法
  • 通过给 WhoSay 类中的 say() 函数添加一个 who 参数,其内部利用传入的 who 调用 say() 方法。这意味着,当调用 WhoSay 类中的 say() 方法时,我们传给 who 参数的是哪个类的实例对象,它就会调用那个类中的 say() 方法。

4、私有变量

单下划线、双下划线、头尾双下划线说明:

  • 1)以双下划线开头,并且以双下划线结尾的,定义的是特殊方法或者变量,一般是系统定义名字 ,比如:
    在这里插入图片描述

  • _foo:以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,
    不能用于 from module import *

  • __foo:双下划线的表示的是私有类型(private)的变量或者方法,只能是允许这个类本身进行访问。
    在类内部的方法中使用时 self.__foo。

  • Python不允许实例化的类直接访问私有数据,会报错:类中无该属性
    但是可以通过使用 object._className__attrName(即:
    对象名._类名__私有属性名 )来访问

 
class JustCounter():
    __secretCount = 0  # 私有变量
    publicCount = 0    # 公开变量
 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print (self.__secretCount)
 
counter = JustCounter()
counter.count()
counter.count()
print (counter.publicCount)


# 报错:JustCounter instance has no attribute '__secretCount',
# 因实例不可以访问私有属性
print (counter.__secretCount)  

 # 可以访问私有属性
print(counter._JustCounter__secretCount)  

5、实例方法、静态方法、类方法

Python实例方法、静态方法和类方法详解(包含区别和用法)

Python类调用实例方法

6、描述符

  • Python 中,通过使用描述符,可以让程序员在引用一个对象属性时自定义要完成的工作。

  • 本质上看,描述符就是一个类,只不过它定义了另一个类中属性的访问方式。换句话说,一个类可以将属性管理全权委托给描述符类。

什么是描述符,Python描述符详解

7、运算符重载

  • 重载运算符,指的是在类中定义并实现一个与运算符对应的处理方法,这样当类对象在进行运算符操作时,系统就会调用类中相应的方法来处理。

在这里插入图片描述
在这里插入图片描述

2、函数

  • 函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化,不需要编写大量重复的代码。

  • 函数可以提前保存起来,并给它起一个独一无二的名字,只要知道它的名字就能使用这段代码。函数还可以接收数据,并根据数据的不同做出不同的操作,最后再把处理结果反馈给我们。

1、形参和实参

Python函数值传递和引用传递(包括形式参数和实际参数的区别)

  • 函数参数的主要限制是:关键字参数必须在位置参数之后(如果有的话)

形参:

  • 形式参数:在定义函数时,函数名后面括号中的参数就是形式参数,

实参:

  • 在调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数。

  • 在调用函数,指定的实际参数的数量,必须和形式参数的数量一致(传多传少都不行),否则 Python 解释器会抛出 TypeError 异常,并提示缺少必要的位置参数。

  • 在调用函数时,传入实际参数的位置必须和形式参数位置一一对应,否则会:
    1)抛出 TypeError 异常
    2)产生的结果和预期不符

Python函数参数传递机制(超级详细)

根据实际参数的类型不同,函数参数的传递方式可分为 2 种:

1)值传递:

  • 适用于实参类型为不可变类型(字符串、数字、元组)

  • 值传递实际上就是将实际参数值的副本(复制品)传入函数,而参数本身不会受到任何影响。

  • 函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;

2) 引用(地址)传递:

  • 适用于实参类型为可变类型(列表,字典)

  • 函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。

  • 引用传递方式的底层实现,采用的依然还是值传递的方式。


# 在传入可变对象时,形参obj只是
def demo(obj) :
    obj += obj
    print("形参值为:",obj)
    
print("-------值传递-----")

a = "C语言中文网"
print("a的值为:",a)

demo(a)
print("实参值为:",a)

'''
主程序首先创建一个列表对象,并定义一个a引用变量(其实就是一个指针)指向该对象。
此时内存中有两个东西:对象本身以及指向该对象的引用变量。

然后主程序开始调用 demo() 函数:
在调用 demo() 函数时,a变量作为参数传入 demo() 函数,

这里依然采用值传递方式:
把主程序中a 变量的值赋给 demo() 函数的 obj 形参,从而完成 demo() 函数的 obj 参数的初始化。
由于主程序中的 a是一个引用变量(也就是一个指针),
它保存了列表对象的地址值,
当把 a 的值赋给demo() 函数的 obj 参数后,系统复制了a变量,
即让demo() 函数的obj 参数也保存这个地址值,
即也会引用到同一个字典对象。

因此当程序在demo() 函数中操作 obj 参数时,
由于 obj 只是一个引用变量,故实际操作的还是字典对象。
此时,不管是操作主程序中的 a 变量,还是操作 demo() 函数里的obj 参数,其实操作的都是它们共同引用的字典对象,它们引用的是同一个字典对象。

'''
print("-----引用传递-----")

a = [1,2,3]  
print("a的值为:",a)

demo(a)
print("实参值为:",a)

输出结果:

-------值传递-----
a的值为: C语言中文网
形参值为: C语言中文网C语言中文网
实参值为: C语言中文网
-----引用传递-----
a的值为: [1, 2, 3]
形参值为: [1, 2, 3, 1, 2, 3]
实参值为: [1, 2, 3, 1, 2, 3]

2、位置参数、关键字参数、默认参数

1、 位置参数

  • 有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,

  • 换句话说,调用函数时传入实际参数的数量和位置都必须和定义函数时保持一致。

1)在调用函数,指定的实际参数的数量,必须和形式参数的数量一致(传多传少都不行),

  • 否则 Python 解释器会抛出 TypeError 异常,并提示缺少必要的位置参数。

2)在调用函数时,传入实际参数的位置必须和形式参数位置一一对应,否则会产生以下 2 种结果:

  1. 抛出 TypeError 异常

当实际参数类型和形式参数类型不一致,并且在函数中,这两种类型之间不能正常转换,此时就会抛出 TypeError 异常。

  1. 产生的结果和预期不符

调用函数时,如果指定的实际参数和形式参数的位置不一致,但它们的数据类型相同,那么程序将不会抛出异常,只不过导致运行结果和预期不符。

2、关键字参数

  • 关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可。
def dis_str(str1,str2):
    print("str1:",str1)
    print("str2:",str2)
#位置参数
dis_str("http://c.biancheng.net/python/","http://c.biancheng.net/shell/")

#关键字参数:混合传参和全用关键字参数
dis_str("http://c.biancheng.net/python/",str2="http://c.biancheng.net/shell/")
dis_str(str2="http://c.biancheng.net/python/",str1="http://c.biancheng.net/shell/")
  • 在调用有参函数时,既可以根据位置参数来调用,也可以使用关键字参数来调用。

  • 在使用关键字参数调用时,可以任意调换参数传参的位置。

  • 混合传参时关键字参数必须位于所有的位置参数之后,否则会报错。

3、默认参数(缺省参数)

  • 在定义函数时,直接给形式参数指定一个默认值。有默认值的参数就叫缺省参数

  • 当定义一个有默认值参数的函数时,有默认值的参数必须位于所有没默认值参数的后面,否则会产生语法错误。

3、不定长参数

跳转到:
3、变量:函数多值参数

4、匿名函数

关键字:

  • lambda
# 根据字符串中不同字母的数量对一个字符串集合进行排序
>>>strings = ['abc', 'aaa', 'cdfg', 'ba']
>>>strings.sort(key=lambda x: len(set(list(x))))
>>>strings

['aaa', 'ba', 'abc', 'cdfg']

5、局部函数

Python局部函数及用法(包含nonlocal关键字)

Python函数使用方法(高级用法)

在函数内部定义函数,此类函数又称为局部函数。

  • 如果所在函数没有返回局部函数,则局部函数的可用范围仅限于所在函数内部;

  • 如果所在函数将局部函数作为返回值,则局部函数的作用域就会扩大,既可以在所在函数内部使用,也可以在所在函数的作用域中使用。

#全局函数
def outdef ():
    name = "所在函数中定义的 name 变量"
    
    #局部函数
    def indef():
        print("调用局部函数")

	    nonlocal name
	    print(name)
	    #修改name变量的值
	    name = "局部函数中定义的 name 变量"
	    
    #调用局部函数
    return indef


#调用全局函数
new_indef = outdef()
# 调用全局函数中的局部函数
new_indef()

  • 不加nonlocal关键字的话,会报错
    UnboundLocalError: local variable ‘name’ referenced before assignment(局部变量 name 还没定义就使用)

  • 不可以通过global关键字解决,因为outdef函数中的name变量是局部变量

  • 加了nonlocal关键字后,后面紧跟的print函数打印的是:所在函数中定义的 name 变量,即outdef函数中定义的name变量的值

6、闭包函数

什么是闭包,Python闭包(初学者必读)

# 计算一个数的n次幂
#闭包函数,其中 exponent 称为自由变量
def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of # 返回值是 exponent_of 函数
square = nth_power(2) # 计算一个数的平方
cube = nth_power(3) # 计算一个数的立方

print(square(2))  # 计算 2 的平方
print(cube(2)) # 计算 2 的立方

输出:
4
8
  1. 外部函数 nth_power() 的返回值是函数 exponent_of(),而不是一个具体的数值。

  2. 在执行完 square = nth_power(2) 和 cube = nth_power(3) 后,外部函数 nth_power() 的参数 exponent 会和内部函数 exponent_of 一起赋值给 squre 和 cube,这样在之后调用 square(2) 或者 cube(2) 时,程序就能顺利地输出结果,

7、exec函数和eval函数

3、变量

Python学习——局部变量全局变量、函数缺省参数、函数多值参数、私有化

class G():
    fly = False #类中的全局变量
    def __init__(self):
        selg._age = 1 #加一个下划线,是一种不成文的规定,意思是该变量是私有变量
        self.long = 2 #普通变量
        self.__width = 3 #有两个下划线,是一种“真”私有变量
    def run(self):
        r = 4 #局部变量
        print("I am running!.")

1、私有变量

【PYTHON】论Python的类中的私有变量、共有变量、全局变量、局部变量

  • 只能被当前类或者方法或者函数才能访问的变量,其他的类或者方法或者函数是不能直接访问的。

  • 这里说不能直接访问,也就是说可以通过间接的方式访问。

私有化:

在这里插入图片描述

2、缺省参数(默认参数)

定义函数时可以给某个参数指定一个默认值,有默认值的参数就叫缺省参数

调用函数时如果没传值,则默认为函数内部定义的默认值
注:

  • 调用函数时如果没传值,则默认为函数内部定义的默认值

  • 带有默认值的缺省参数必须要放在参数列表的末尾

  • 调用函数时如果有多个缺省参数,需要制定参数名,这样解释器才能知道对应关系

3、函数多值参数(*args和**kargs)

Python中的*args和**kwargs是什么?该如何使用?

函数在不确定个数参数时会用到多值参数

python中有两种多值参数:

  1. 参数名前加一个 * 可以接收元组
  2. 参数名前增加两个 * 可以接收字典。

一般给多值参数命名时,习惯使用以下两个名字

1)*args:存放元组参数,前面有一个 *
2) * *kwargs:存放字典的参数,前面有两个

args是arguments的缩写,有变量的含义

kw是keyword的缩写,kwargs可以记忆键值对参

args 是 arguments 的缩写,表示位置参数;

kwargs 是 keyword arguments 的缩写,表示关键字参数

  • *args 用来将参数打包成tuple(元组)给函数体调用,*用来创建元组,args是位置参数
def function(x, y, *args):
    print(x, y, args)

function(1, 2, 3, 4, 5)

输出:

1 2 (3, 4, 5)
  • **kwargs 用来将关键字参数打包成dict(字典)给函数体调用,双星号创建了字典,kwargs是关键字参数
def function(**kwargs):
    print(kwargs)

function(a=1, b=2, c=3)

输出:

{'a': 1, 'b': 2, 'c': 3}
  • arg就是指一个参数
  • 参数arg、*args、**kwargs三个参数的位置必须是一定的。必须是(arg,*args,**kwargs)这个顺序,否则程序会报错。
  • *args 必须放在 **kwargs 的前面,因为位置参数在关键字参数的前面。
def function(arg,*args,**kwargs):
    print(arg,args,kwargs)

function(6,7,8,9,a=1, b=2, c=3)

输出:

6 (7, 8, 9) {'a': 1, 'b': 2, 'c': 3}

4、全局变量、局部变量

Python之全局变量、局部变量、类变量、实例变量的解读

全局变量:在模块内、所有函数外、所有class外

  • 全局变量的默认作用域是整个程序,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。
  • 定义全局变量的方式有以下 2 种:
    1)在函数体外定义的变量,一定是全局变量
    2)在函数体内定义全局变量。即使用 global 关键字对变量进行修饰后,该变量就会变为全局变量。

局部变量:在函数内、在class的方法(类方法、静态方法、实例方法)内,且变量前面没有修饰

  • 在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,我们将这样的变量称为局部变量(Local Variable)。

  • 当函数被执行时,Python 会为其分配一块临时的存储空间,所有在函数内部定义的变量,都会存储在这块空间中。而在函数执行完毕后,这块临时存储空间随即会被释放并回收,该空间中存储的变量自然也就无法再被使用。

# 【全局变量】与【局部变量】举例

ahead = 'HelloWorld' #ahead是全局变量
showList = []        #showList也是全局变量
 
def print_ahead():
    print(ahead)   #在该函数中没有称为ahead的局部变量,此处实际调用的是全局变量ahead
 
def print_other():
    city = 'beijing'       #city是局部变量
    print(city + ahead)  #局部变量 与 全局变量 拼接
 
def print_list():
    global showList    #global关键字表示引用的是全局变量showList
                    
    showList.append(1)
    showList.append(2)
    print(showList)
 
print_ahead()
print_other()
print_list()

Python变量作用域(全局变量和局部变量)

获取指定作用域范围中的变量:

1)globals()函数

  • 返回一个包含全局范围内所有变量的字典,该字典中的每个键值对,键为变量名,值为该变量的值。

  • 通过该字典,我们还可以访问指定变量,甚至如果需要,还可以修改它的值。

2)locals()函数

  • 在函数内部调用 locals() 函数,会获得包含所有局部变量的字典;

  • 在全局范围内调用 locals() 函数,其功能和 globals() 函数相同。

  • 当使用 locals() 函数获得所有局部变量组成的字典时,可以向 globals()
    函数那样,通过指定键访问对应的变量值,但无法对变量值做修改。

3)vars(object)

  • 返回一个指定 object 对象范围内所有变量组成的字典。

  • 如果不传入object 参数,vars() 和 locals() 的作用完全相同。


#全局变量
Pyname = "Python教程"
Pyadd = "http://c.biancheng.net/python/"
def text():
    #局部变量
    Shename = "shell教程"
    Sheadd= "http://c.biancheng.net/shell/"
    print("函数内部的 locals:")
    print(locals())
    
    print('locals内部['Shename']的值:', locals()['Shename'])
    locals()['Shename'] = "shell入门教程"
    print('修改后locals内部['Shename']的值:', Shename)
    


print("globals:")
print(globals())


print('globals['Pyname']:', globals()['Pyname'])
globals()['Pyname'] = "Python入门教程"
print('修改后globals['Pyname']:', Pyname)


text()
print("函数外部的 locals:")
print(locals())

输出:

函数内部的 locals:
{'Sheadd': 'http://c.biancheng.net/shell/', 'Shename': 'shell教程'}


locals内部['Shename']的值:shell教程
修改后locals内部['Shename']的值:shell教程


globals:
{ ...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ......}

globals['Pyname']:Python教程
globals['Pyname']:Python入门教程


函数外部的 locals:
{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... }

5、类变量、实例变量

类变量:在class内,不在class的任何方法内

实例变量:在class的方法内,且使用self修饰的变量

# 【类变量】与【实例变量】举例

class Person(object):
    TAG = "Person"               #TAG是类变量
    def __init__(self, name):    #self表示当前实例对象
        print(Person.TAG)      #类变量TAG被访问
        self.personName = name   #personName是实例变量
 
    def print_name(self):
        group = "BeiJing_"      #group是局部变量
        print(group + self.personName)  # 局部变量 与 实例变量 拼接
 
if __name__ == "__main__":
    p = Person("WangYuanWai")
    p.print_name()

4、装饰器

概念:是一个闭包,把一个函数当做参数返回一个替代版的函数,本质上就是一个返回函数的函数。通过装饰器函数,可以在不修改原函数的前提下,来对函数的功能进行合理的扩充

  • 当调用的代码不可更改时,可以加装饰器改

1)简单的装饰器:

#简单的装饰器
def func1():
    print('sunck is a good man')

def outer(func):
   def inner():
       print("********")
       func()
   return  inner()

f =  outer(func1)

输出:
在这里插入图片描述
2)@语法只是将函数传入装饰器函数

把@outer放在say函数前面,相当于执行了语句:

  • say = outer(say())
#通用装饰器

def outer(func):
    def inner(*args,**kwargs):
        #添加修改的功能
        print('&&&&&&&')
        func(*args,**kwargs)
    return inner

@outer  #say = outer(say)
def say(name,age):  #函数的参数理论上是不受限制的,但实际上不要超过6、7个
    print('my name is %s,my age is %d'%(name,age))

say('y',20)

输出:
在这里插入图片描述
装饰器
Python @函数装饰器及用法(超级详细)

def funA(fn):
    # 定义一个嵌套函数
    def say(arc):
        print("Python教程:",arc)
    return say

@funA
def funB(arc):
    print("funB():", a)
funB("http://c.biancheng.net/python")

输出:

Python教程: http://c.biancheng.net/python

通过 funB() 函数被装饰器 funA() 修饰,funB 就被赋值为 say。这意味着,虽然我们在程序显式调用的是 funB() 函数,但其实执行的是装饰器嵌套的 say() 函数。

def funA(fn):
    # 定义一个嵌套函数
    def say(*args,**kwargs):
        fn(*args,**kwargs)
    return say

@funA
def funB(arc):
    print("C语言中文网:",arc)

@funA
def other_funB(name,arc):
    print(name,arc)
funB("http://c.biancheng.net")
other_funB("Python教程:","http://c.biancheng.net/python")

输出:

C语言中文网: http://c.biancheng.net
Python教程: http://c.biancheng.net/python

5、迭代器、生成器

Python3 迭代器与生成器

1、迭代器

  • 迭代器协议是一种令对象可遍历的通用方式。

  • 迭代器是访问集合元素的一种方式。
    迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
    迭代器只能往前不会后退。

  • 迭代器是一个可以记住遍历的位置的对象。迭代器是一种用于在上下文中(比如for循环)向python解释器生成对象的对象。大部分以列表或列表型对象为参数的方法都可以接受任意的迭代器对象。包括内建方法比如min、max和sum,以及类型构造函数比如list和tuple。

基本方法:

  • iter() :创建迭代器对象
  • 字符串,列表或元组对象都可用于创建迭代器
  • next():遍历迭代器
list1=[1,2,3,4]
it1 = iter(list)    # 创建迭代器对象
print (next(it))  #输出值为:1
print (next(it))  #输出值为:2


#使用for语句进行遍历:
list2=[5,6,7,8]
it2 = iter(list)    # 创建迭代器对象
for x in it2:
    print (x, end=" ")
    
# 输出:5 6 7 8


# 使用next()函数遍历
import sys         # 引入 sys 模块
 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
 
while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()   # 程序直接退出,不捕捉异常

#输出:
1
2
3
4

Python退出程序 sys.exit()

  • range函数返回一个迭代器,该迭代器返回一个等差整数序列(左闭右开)
    range(10)

2、生成器

  • 在 Python 中,使用了yield 的函数被称为生成器(generator)。

  • 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。调用一个生成器函数,返回的是一个迭代器对象

  • 生成器是构造新的可遍历对象的方式。普通函数执行并一次返回单个结果,而生成器‘惰性’地返回一个多结果序列,在每一个元素产生之后暂停,直到下一个请求。

  • 如需创建一个生成器,只需要在函数中将返回关键字return替换为yield关键字。

  • 当实际调用生成器时,代码并不会立即执行,直到请求生成器中的元素时,才会执行

  • 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

# 使用 yield 实现斐波那契数列:
import sys
 
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1


f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()   # 程序直接退出,不捕捉异常

# 输出结果为:0 1 1 2 3 5 8 13 21 34 55

生成器表达式:

  • ( x+1 for x in range(100) )

  • 可以作为函数参数用于替代列表推导式,比如:
    sum( x+1 for x in range(100) )

# 两种生成器是等价的
gen = ( x+1  for   x  in  range(100)def make_gen():
	for x in range(100):
		yield x+1
gen = make_gen()

3、比较return与yield

python-复盘-yield & return区别

共同点:

  • return和yield都用来返回值;

  • 在一次性地返回所有值场景中return和yield的作用是一样的。

不同点:

  • 如果要返回的数据是通过for等循环生成的迭代器类型数据(如列表、元组),
    return只能在循环外部一次性地返回,
    yeild则可以在循环内部逐个元素返回

  • yiled要迭代到哪个元素那个元素才即时地生成,
    而return要用一个中间变量result_list保存返回值,当result_list的长度很长且每个组成元素内容很大时将会耗费比较大的内存,此时yield相对return才有优势。

  • return 是函数返回值,当执行到return,后续的逻辑代码不在执行。
    yield是创建迭代器,可以用for来遍历,有点事件触发的意思

  • yield一般与循环一起用,相当于生成了一个容器(常见的就是字典),然后在这个容器里面存放了每次循环以后的值,并且就在那放着,不输出,不返回,等你下次需要他的时候直接取出来用就行(要用next和send函数调用)

#encoding:UTF-8  
def yield_test(n):  
    for i in range(n):  
        yield call(i)     # 它会立即把call(i)输出,成果拿出来后才会进行下一步,所以 i, ',' 会先执行
        print("i=",i)     # 后执行
    #做一些其它的事情      
    print("do something.")        #  待执行,最后才执行一遍
    print("end.")  
  
def call(i):  
    return i*2  
  
#使用for循环  
for i in yield_test(5):  
    print(i,",")   # 这里的 i 是 call(i)
>>>        # 输出的结果
0 ,  
i= 0  
2 ,  
i= 1  
4 ,  
i= 2  
6 ,  
i= 3  
8 ,  
i= 4  
do something.  
end.  
>>> 
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-06 10:59:24  更:2021-09-06 11:00:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:51:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码