Python——前言(模块、import、注释、标识符、关键字、引用、拷贝、不成文规定、Python2和Python3、Python与C++及Java)

Python前言

  • Python——前言(使用的是Python3.X版本)
    • 1、Python的特点
    • 2、Python模块
      • 基本介绍
      • 导入模块的方式
      • 导入自己编写的.py文件
      • 关于import
        • absolute import
        • relative import
    • 3、Python pip命令
      • pip工具
      • pip常用命令
      • pip命令运行实例
    • 4、Python注释
    • 5、Python标识符
    • 6、Python关键字
    • 7、Python的"引用"与"赋值操作"
    • 8、Python的深拷贝与浅拷贝
      • 对可变对象进行浅拷贝
      • 对可变对象进行深拷贝
    • 9、Python的不成文规定
    • 10、Python2和Python3的区别[^1]
    • 11、Python与C++及Java进行比较

Python——前言(使用的是Python3.X版本)

1、Python的特点

Python不仅是一门脚本语言,也是一门跨平台、开源、免费的解释型高级动态编程语言,被称为胶水语言,可以把多种不同语言编写的程序融合到一起,实现无缝拼接,支持命令式编程和函数式编程两种模式,拥有大量的几乎支持所有领域应用开发的成熟第三方扩展,Python为了体现代码之间逻辑关系,使用缩进来对代码进行分组(一般来说,使用4个空格作为基本缩进单位,虽然也可以使用3个空格作为缩进单位,但不建议那样做)

2、Python模块

基本介绍

Python的模块可以是一个以.py结尾的Python文件,可以是含有多个py文件的文件夹,或者是已被编译为共享库或DLL的c或C++扩展,抑或是使用C编写并链接到python解释器的内置模块,模块能定义函数、类和变量,模块里也能包含可执行的代码,只需要 import module_name 一行代码就可以打开文件 module_name.py ,并将 module_name.py 中的所有函数都复制到 import module_name 这行代码所在的Python文件中

Python的包就是一种特殊的Python模块,但是包往往对应的是一个文件夹

导入模块的方式

  • 从模块中导入Python函数有三种方式
    • from module_name import function_name
      将导入该模块中的function_name这一个函数,这种导入方式下的Python函数,以function_name()形式使用

    • import module_name
      将导入整个模块,这种导入方式下的Python函数,以module_name.function_name()形式使用

    • from module_name import*
      将导入模块中的所有函数,不建议使用,一方面这样会减低代码的可读性,无法区分某一个函数为自定义函数还是从模块中导入的函数;另一方面,这种导入对象的方式会导致命名空间的混乱,如果多个模块中具有同名函数,只有最后一个导入的模块中的那个函数是有效的,其他模块中的同名函数都无法访问
      这种导入方式下的Python函数,以function_name()形式使用

如果如果要导入的函数名可能与程序中现有的名称冲突, 或者函数名太长,则可以在导入时使用 as 进行改名,值得注意的是改名以后,使用原来的名称就无法实现正常访问了

导入自己编写的.py文件

解释器执行import语句的工作流程:

  • 先判断这个模块是不是built-in即内建模块
    • 如果是则直接引入内建模块
    • 如果不是则Python解释器会查看sys.path变量(sys.path在.py文件执行时动态生成)中存储的路径信息找对应的模块

于是我们可以通过向sys.path添加路径来实现.py文件的导入
sys.path是个列表类型对象,可以使用append方法将指定路径进行添加

sys.path.append(r'C:\编程\Python代码\Pytest\Pytest') 
import Pytest       #自己编写的.py文件

sys.path中记录的路径有以下几种:

  • 1.在当前目录,即当前执行的程序文件所在目录下查找
  • 2.环境(用户)变量PYTHONPATH指定的目录, 即.bash_profilec
  • 3.到 Python 默认的安装目录下查找
    寻找的时候,会按照标准模块 sys 的 sys.path 变量保存的顺序去寻找,一旦找到了,就立即停止下来,所以要注意命名冲突的问题

通过打印标准模块 sys 的 sys.path 变量我们可以看到支持查找的所有目录。换句话说,如果要导入的模块没有存储在 sys.path 显示的目录中,那么导入该模块并运行程序时,Python 解释器就会抛出 ModuleNotFoundError(未找到模块)异常

关于import

import就是一个把文件系统中的文件或者文件夹变成Python的模块或者包的过程
import可以分为absolute import和relative import

absolute import:可以根据一个确定的名称去寻找模块,建立模块,并把模块赋值给一个变量。可以类比于通过文件路径中的绝对路径找到一个东西

relative import:根据一个包中的不同模块的相对位置进行模块间的寻找,建立模块,并把模块赋值给一个变量。可以类比于通过文件路径中的相对路径找到一个东西

下面的例子均以import XXX为例进行讲解

absolute import

import的是一个文件(即一个模块)

  • 重点内容

    • 在执行import的时候,会去寻找名字XXX对应的文件,首先会检查自己的缓存,去寻找是否有以及被读取的对应名称模块,如果有的话,就会直接赋值给import语句中的XXX
    • 如果没有,则会先查看其是否为内建模块,如果不是,则按照sys.path保存的路径顺序去寻找,在寻找到这个文件的时候,会在一个单独的命名空间中运行这个文件,即建立一个模块的过程,模块是一个容器,内部可以包含其他对象,在完成模块的建立的时候,会更新缓存,为了再次import这个文件的时候,就不需要再次进行模块的建立了(比如,连续import同一个文件,且文件中具有打印操作,你会发现只会进行一次打印),最后,将模块赋值给import语句中的XXX
    • 在import XXX以后,XXX可以作为一个变量使用了,即我们通过XXX找到了(或者建立了)模块,然后我们又将模块赋值给了XXX
  • 拓展内容

    • 如果使用的是import XXX as YYY,则一整个过程是通过XXX找到了(或者建立了)模块,然后我们又将模块赋值给了YYY,而XXX是未定义
    • 如果使用的是from XXX import A,则一整个过程是是通过XXX找到了(或者建立了)模块,然后我们在这个模块中找到名称为A的对象,赋值给这个A
    • 如果使用的是from XXX import A as B,则一整个过程是是通过XXX找到了(或者建立了)模块,然后我们在这个模块中找到名称为A的对象,最后赋值给了这个B

import的是一个文件夹(即一个包)

  • 重点内容
    • 由于包就是特殊的模块,于是import 包的过程和import 模块的过程是非常相似的,唯一的一个区别就是,在import 模块的时候,我们会将对应的文件代码在一个单独的命名空间中运行了一下,去创建模块,但是在import 包的时候,我们是会去查看这个文件夹下面是否有一个名称为__init__.py的Python文件,如果没有,则不会运行任何Python代码(当然,import XXX的语句还是可以正常运行的)
    • 如果有,则会在一个单独的命名空间中对该文件进行运行,然后使用该命名空间构成一个模块,这个模块就是一个特殊的模块,我们称为包,也就是说,其实也只是导入了一个.py文件,只不过我们是通过寻找名称为XXX的文件夹,然后,导入的名称为__init__.py的Python文件,使用该文件运行,并建立了一个模块,赋值给了XXX罢了
    • 如果使用的是import XXX.H,我们就可以实现importXXX文件夹下面的其他模块(当然,你可以.多次,即可以写成import XXX.H.I.J,即最终导入的是名称为J的模块),这个过程和前面还是类似的,即先找到XXX文件夹,接着去寻找到H名称的文件,然后把XXX.H作为变量,把模块赋值给它
    • 除了这些,在一整个过程中其实也会隐性地将XXX以import XXX的方式进行导入,即会把XXX文件夹下面的名称为__init__.py的Python文件进行导入,如果是以import XXX.H.I.J的方式进行模块导入,则相应地,也会隐性地将H文件夹中的名称为__init__.py的Python文件以import XXX.H的形式导入,将I文件夹中的的名称为__init__.py的Python文件以import XXX.H.I的形式导入,即以import XXX.H.I.J的方式进行模块导入,实际上是导入了4个模块,如果每一个文件夹下都具有一个名称为__init__.py的Python文件的话,同时在对每一个被导入的模块进行dir查询属性的时候,是可以看到被导入的子模块的,比如dir(XXX),可以看到H,dir(XXX.H),可以看到I,dir(XXX.H.I),可以看到J
  • 拓展内容
    • 如果使用的是import XXX.H.I.J as K,则会导致XXX不会被导入(自然XXX.H,XXX.H.I也不会被导入),只有XXX.H.I.J对应的模块被找到,并赋值给了K(即XXX.H.I.J也是未定义的状态)
relative import

absolute import这种模块导入方式的缺点在于,如果中间文件夹的名称发生了变化,就无法找到目标模块了,或者从另一个角度,如果目标模块在很多层的文件夹下,拿import的时候,就要加上非常多的".",代码也会比较复杂
有时我们需要在同一个包中的不同模块之间进行相互导入,这些模块的相互关系是极其稳定的,因为它们同处于一个包中(即一个文件夹中),模块的相对位置不容易发生变化

  • 重点内容
    • 通过from .XXX import YYY,我们可以实现通过本文件(即要执行from .XXX import YYY语句的文件)导入与自身位于同一个包中的名称为XXX的模块中的YYY对象,整个过程是先通过模块的__name__属性和__package__属性找到包(即本本文件和XXX文件所在的文件夹),然后将from .XXX import YYY转化为from 包名.XXX import YYY,即本质就是将一个relative import转化为了一个absolute import,如果直接进行本文件的运行,就会导致此时文件的__name__属性为‘main’而__package__属性为None,,解释器就找不到父级包的任何信息,因此报错
    • 使用from …XXX import YYY,我们可以通过本文件(即要执行from .XXX import YYY语句的文件)导入位于上一级包的名称为XXX的模块中的YYY对象,这个时候,类似地,就需要上一级包也是可以被找到的

3、Python pip命令

pip工具

easy install 和 pip 工具是管理 Python 扩展库的主要方式,其中 pip 用得更多一点
使用 pip 不仅仅可以查看本机已安装的 Python,还可以极其方便地实现 Python 扩展库的安装、升级和卸载等操作

pip常用命令

pip命令 解释
pip freeze > requirements.txt 将本Python环境中已安装的模块消息快速生成为一个名为requirements.txt的文件(符号>表示进行重定向)
pip install -r requirements.txt 从名称为requirements.txt的文件中读取所需安装的扩展库消息并自动进行安装
pip list 列出当前环境中的所有已安装的第三方扩展库消息
pip freeze 列出当前环境中的所有已安装的第三方扩展库(仅仅就是与pip list打印方式不同)
pip install 第三方模块名==版本号 使用pip工具安装指定版本的第三方扩展库
pip install 第三方模块名==版本号 -i https://pypi.tuna.tsinghua.edu.cn/simple 使用pip工具安装扩展库时,可以使用-i选项指定从国内服务器上下载和安装,从而实现速度大幅度提高

其中的命令"pip freeze > requirements.txt"和命令"pip install -r requirements.txt"一般联合使用,即同学A交给同学B使用命令pip freeze > requirements.txt得到的具有该项目的环境下所有第三方模块信息的requirements.txt,B直接使用命令pip install -r requirements.txt进行这些模块的安装,使得两个同学的Python虚拟环境完全相同

pip命令运行实例

pip freeze > requirements.txt

Python——前言(模块、import、注释、标识符、关键字、引用、拷贝、不成文规定、Python2和Python3、Python与C++及Java)_第1张图片

pip list

Python——前言(模块、import、注释、标识符、关键字、引用、拷贝、不成文规定、Python2和Python3、Python与C++及Java)_第2张图片

 pip freeze

Python——前言(模块、import、注释、标识符、关键字、引用、拷贝、不成文规定、Python2和Python3、Python与C++及Java)_第3张图片

4、Python注释

注释能够大大增强程序的可读性,在代码关键部位加上注释是必要的,方便程序员的理解和阅读
单行注释以井号 (#)开头,在物理行末尾截止
多行注释使用三单引号(‘’'),三双引号(“”"),一般用在注释一段代码的情况
解释器不执行注释内容,注释可以在任何位置

5、Python标识符

Python标识符可以由汉字、英文字母、下划线、数字组成,不能以数字开头。标识符对英文字母的大小写敏感,例如student和Student是不同的变量名

  • 以下划线开头的变量在Python中有特殊含义
    • 以单下划线开头的标识符表示不能直接访问的类属性,需要通过类提供的接口进行访问,即不能使用from module_name import* 方式导入使用
    • 以两个(或更多)下划线开头,而不以两个(或更多)下划线结尾的标识符表示类的私有成员
    • 同时以双下划线开头和结果的标识符表示Python中的特殊方法

不建议使用系统内置的模块名、类型名或函数名以及已导入的模块名及其成员名作变量名,这将会改变其类型和含义(即如果使用了,会导致原来的内置函数无法使用),可以通过dir(builtins)查看所有内置模块、类型和函数

不能使用关键字作变量名,会抛出异常,可以导入keyword模块后使用print(keyword.kwlist)查看所有Python关键字

6、Python关键字

  • pass
    • 提供占位作用,保证程序运行不会报错,可以用来为以后的软件升级预留空间
  • assert
    • 断言,用来确定一个条件必须满足(即判断一个表达式结果的布尔值为True),可用来帮助调试程序,如果不满足(表达式结果的布尔值为False)会抛出异常
  • class
    • 用来定义类对象
  • as
    • 在import、with或者except语句中给对象起别名
  • del
    • 用来使某一个对象的引用计数减一
  • raise
    • 用来显式地抛出异常
  • def、return
    • 分别用来定义函数,返回自定义函数的结果
  • in、is、or、not、and
    • 为Python运算符
  • try、except、finally
    • 在异常处理机制中出现
  • if、else、elif、for、while、continue、break
    • 在程序的组织结构中出现
  • lambda
    • 用于lambda表达式,构造匿名函数
  • await、async
    • 语法糖
  • from、import
    • 在模块导入中出现
  • global、nonlocal
    • 分别用来声明变量为全局变量,非局部变量
  • True、False
    • 布尔类型对象
  • None
    • 空类型对象,如果对一个无返回值的函数取值,则得到None
  • yield
    • 用于yield语句,构成生成器
  • with
    • 用于上下文管理语句,使用上下文管理器
  • __peg_parser__
    • 一个Python3.9的保留字彩蛋,3.8和3.10都没有

7、Python的"引用"与"赋值操作"

给变量赋值None,表示该变量没有指向任何有效东西,其实也就是开发者在写这个代码的时候,也不知道该变量应该指向哪一个对象,或者感觉指向哪一个对象都不太合适

8、Python的深拷贝与浅拷贝

对可变对象进行浅拷贝

Python中的拷贝一般都是浅拷贝,拷贝时,对象包含的子对象不会重新创建一份,而是把该可变对象保存的子对象的引用直接复制过来(即拷贝出来的可变对象和原来对象的子对象是完全相同的,源对象和拷贝对象会引用同一个子对象)

class CPU:          #一个CPU类
    pass

class Computer:     #一个电脑类
    def __init__(self,cpu):
        self.cpu=cpu            #将传入的CPU类的实例对象引用赋值给实例属性cpu

cpu = CPU()                     #实例化出一个CPU类的实例对象
computer = Computer(cpu)        #传入一个CPU类的实例对象,实例化出一个Computer类的实例对象

computer2=copy.copy(computer)   #对Computer类的实例对象进行浅拷贝

print(id(computer),id(computer.cpu))        #打印原来的Computer类的实例对象引用 和 其中的CPU类的实例对象引用
print(id(computer2),id(computer2.cpu))      #打印拷贝后的Computer类的实例对象引用 和 其中的CPU类的实例对象引用

"""
输出结果:
1784174127040 1784174127088
1784189454656 1784174127088
"""

代码解析:

  • 拷贝后的Computer类的实例对象引用 和 原来的Computer类的实例对象引用是不同的,说明是不同的Computer类的实例对象
  • 拷贝后的Computer类的实例对象中的CPU类的实例对象引用 和 原来的Computer类的实例对象中的CPU类的实例对象引用是相同的,说明是同一个CPU类的实例对象

对可变对象进行深拷贝

使用copy模块的deepcopy函数,会递归拷贝对象中包含的子对象(即被拷贝对象中存储的子对象也会被重新创建)。值得注意的是,当子对象皆为可变对象时,源对象和拷贝对象引用的子对象才会不相同(即两个对象存储的id是不同的),如果子对象为不可变对象,是不会创建一个新的子对象的

如果子对象为可变对象

在这里插入代码片

如果子对象为不可变对象

class Computer:     #一个电脑类
    def __init__(self,cpu):
        self.cpu=cpu            #将传入的不可变对象(以字符串为例)引用赋值给实例属性cpu

computer = Computer("CPU")        #传入一个不可变对象(以字符串为例),实例化出一个Computer类的实例对象

computer2=copy.deepcopy(computer)   		#对Computer类的实例对象进行深拷贝

print(id(computer),id(computer.cpu))        #打印原来的Computer类的实例对象引用 和 其中的不可变对象(以字符串为例)
print(id(computer2),id(computer2.cpu))      #打印拷贝后的Computer类的实例对象引用 和 其中的不可变对象(以字符串为例)

"""
输出结果:
2001952352336 2001952336752
2001967483056 2001952336752
"""

9、Python的不成文规定

"不成文规定"是指即使不这样做,程序也可以正常运行(即这些规定并不是硬性要求),但是在专业人士看来,不这样做非常地别扭

类中的方法之间空两行
类和类之间、函数和函数之间空一行
实例方法中的第一个形参取名为cls
类方法中的第一个形参取名为self
实例方法使用实例对象去调用
类方法使用类对象去调用

10、Python2和Python3的区别1

Python 2.x 和 Python 3.x 这两大系列之间的语法相差很大,互不兼容

  • 整型(int)和长整型(long)
    • Python2 中区分,并且使用 int 作为默认的数字类型,数字大小超过一定范围以后会变成 long 类型
    • Python3 不进行区分,只有 int 类型
  • 字符串的格式化方法"f-字符串"
    • 在 Python2 中不存在
    • 在 Python3 中存在
  • 关键字(下面列出了两个版本关键字的不同之处)
    • Python2.7.18 的关键字 print exec
    • Python3.11.1 的关键字 False await None True nonlocal async
  • 默认编码
    • Python2 中为 ASCII 码
    • Python3 中为 UTF-8
  • print
    • Python2 的 print 不一定要以函数形式进行使用,可以使用 print “hello” 的方式输出
    • Python3 的 print 必须以函数形式进行使用,print 语句被 Python3 废弃
  • 新式类和经典类
    • Python2 里面只有写明继承了 object 的类才是新式类,其他的是经典类(就是一些很老的类,自带的功能较少)
    • Python3 里面默认所有类都是继承的 object(即在创建一个类的时候是否有写"(object)"都一样,默认都继承了object类),所以 python3 中的所有类都属于是 Python2 中的新式类
  • 语法上必要的缩进
    • Python2 的缩进机制中,1 个 tab 和 8 个 space 是等价的,所
      以在缩进中可以同时允许 tab 和 space 在代码中共存
    • Python3 使用更加严格的缩进, 1 个 tab 只能找另外一个 tab 替代,因此 tab 和 space 共存会导致报错
  • 不相等操作符
    • Python2 中具有不相等操作符"<>"
    • 不相等操作符"<>“被 Python3 废弃,统一使用”!="
  • 比较操作符
    • Python2 中任意两个对象都可以比较
    • Python3 中只有同一数据类型的对象可以比较
  • 除法操作符"/"
    • Python2 中若为两个整形数进行运算进行的是地板除,但若两个数中有一个为浮点数,则进行的是真除
    • Python3 中整数相除为真除
  • 使用 for 循环时的同名变量值
    • Python2,for 循环会修改外部相同名称变量的值
    • Python3,for 循环不会修改外部相同名称变量的值
  • round 函数不进行精度指定时的返回值类型
    • Python2,round 函数返回 float 类型对象
    • Python3,round 函数返回 int 类型对象

11、Python与C++及Java进行比较

  • Python 和 C++
    • 相同点
      • C++和Python都是面向对象的高级程序设计语言
    • 不同点
      • C++ 是编译型语言,Python 是一门解释型语言
      • C++ 需要手动开辟、手动释放内存,Python 可以自动管理内存
      • C++ 是一种静态类型语言,Python 是一种动态类型语言,即 Python 在编程时,永远不用给任何变量指定数据类型,并且可以随时改变数据类型,而 C++ 写程序时必须声明所有变量的数据类型
      • C++ 运行效率高于Python
  • Python 和 Java2
    • 相同点
      • Java和Python都是面向对象的高级程序设计语言
      • Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其 它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问, 与 Java 类似
      • Python 跟 Java 一样是强类型语言,也就是说它不会根据环境变化自动改变数据类型,不同类型的数据操作时,必须经过强制类型转换为同一类型才能进行运算(弱类型语言在运行时会做隐式做数据类型转换,无需指定类型,默认均为字符型;参与计算会自动隐式转换类型;变量无需事先定义可直接调用)
    • 不同点
      • Java 运行效率高于 Python,尤其是纯 Python 开发的程序,运行效率极低
      • Java 开发偏向于软件工程,团队协同,Python 更适合小型开发
      • Java 是一种静态类型语言,Python 是一种动态类型语言,即 Python 在编程时,永远不用给任何变量指定数据类型,而 Java 写程序时必须声明所有变量的数据类型
      • Java 里的块用大括号对表示,Python 以冒号 + 四个空格缩进表示
      • Java 每行语句以分号结束,Python 可以不写分号
      • 实现同一功能时,Java 要敲的键盘次数一般要比 Python 多
      • Python 函数在传参的时候,形式参数的值可以通过关键字指定,而 Java 方法不可以。Python 函数有默认值参数,而 Java方法 没有
      • Python 的函数定义也可以嵌套,而 Java 不可以

  1. 此处关于Python2和Python3的区别的描述参考了Pang文同学的该博客以及Wangsh@同学的该博客 ↩︎

  2. 此处关于Python和Java区别的描述参考了shengjk1同学的该博客 ↩︎

你可能感兴趣的:(python)