Python学习笔记(三)之面向编程 异常处理 文件处理

系列文章目录

第一章 Python 基本概念
第二章 Python 序列 控制语句 函数
第三章 Python 面向编程 异常处理 文件处理
第四章 Python 坦克大战
第五章 Python 并发编程 网络通信


文章目录


前言

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


一、面向编程

1.面向对象和面向过程的区别

Python完全采用了面向对象的思想,是真正面向对象的编程语言,完全支持面向对象的基本功能,例如:继承、多态、封装等。面向对象编程将数据和操作数据相关的方法封装到对象中,组织代码和数据的方式更加接近人的思维,从而大大提高了编程的效率。
面向对象(Oriented-Object)思想更契合人的思维模式。我们思考的是"怎么设计这个事物?”。比如思考造车,我们就会先思考“车怎么设计?”,而不是“怎么按步骤造车的问题”。这就是思维方式的转变。
面向对象和面向过程总结:

  • 都是解决问题的思维方式,都是代码组织的方式。
  • 面向过程是一种“执行者思维",解决简单问题可以使用面向过程
  • 面向对象是一种“设计者思维”,解决复杂、需要协作的问题可以使用面向对象
    面向对象离不开面向过程:宏观上:通过面向对象进行整体设计,执行和处理数据,仍然是面向过程。

2.类的定义

类可以看做是一个模版,或者图纸,系统根据类的定义来造出对象。类:叫做class。 对象:叫做object,instance(实例)。说某个类的对象,某个类的实例是一样的意思。
属性和方法
通过类定义数据类型的属性(数据)和方法(行为),即“类将行为和状态打包在一起”。


对象是类的具体实体,一般称为“类的实例”。类看做“饼干模具”,对象就是根据这个“模具”制造出的“饼干”。从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态)。更具体一点:“方法代码是共享的,属性数据不共享”。

定义类的语法格式:

class 类名:
类体

  • 类名必须符合“标识符”的规则;一般规定,首字母大写,多个单词使用“驼峰原则”。
  • 类体中我们可以定义属性和方法
  • 属性用来描述数据,方法(即函数)用来描述这些数据相关的操作
class Student:
  def __init__(self,name,score): #构造方法第一个参数必须为self
    self.name = name     #实例属性
    self.score = score
​
  def say_score(self):      #实例方法
    print("{0}的分数是{1}".format(self.name,self.score))
​
s1 = Student('张三',80) #s1是实例对象,自动调用__init__()方法
s1.say_score()

对象完整内存结构
类是抽象的,也称之为“对象的模板”。需要通过类这个模板,创建类的实例对象,然后才能使用类定义的功能。一个Python对象包含三个部分:id(identity识别码)、type(对象类型)、value(对象的值)。其中的值又可以包含属性和方法:

3.构造函数

初始化对象,我们需要定义构造函数__init__()方法。构造方法用于执行“实例对象的初始化工作”,即对象创建后,初始化当前对象的相关属性,无返回值。
要点:

  • 名称固定,必须为:init()
  • 第一个参数固定,必须为self。 self指的就是刚刚创建好的实例对象
  • 构造函数通常用来初始化实例对象的实例属性,如下代码就是初始化实例属性:name和score
    def init(self,name,score):
    self.name = name #实例属性
    self.score = score
  • 通过“类名(参数列表)”来调用构造函数。调用后,将创建好的对象返回给相应的变量。 比如:s1 = Student(‘张三’, 80)
  • init()方法:初始化创建好的对象,初始化指的是:“给实例属性赋值”
  • new()方法: 用于创建对象,但我们一般无需重定义该方法
  • 如果我们不定义__init__方法,系统会提供一个默认的__init__方法。如果我们定义了带参的__init__方法,系统不创建默认的__init__方法

Python中的self相当于C++中的self指针,JAVA和C#中的this关键字。Python中,self必须为构造函数的第一个参数,名字可以任意修改。但一般惯例,都叫做self。

4.实例属性

  • 实例属性一般在__init__()方法中通过self.实例属性名 = 初始值定义
  • 在本类的其他实例方法中,也是通过self进行访问: self.实例属性名
  • 创建实例对象后,通过实例对象访问: obj01 = 类名() #创建和初始化对象,调用__init__()初始化属性
  • obj01.实例属性名 = 值 #可以给已有属性赋值,也可以新加属性

5.实例方法

实例方法是从属于实例对象的方法。实例方法的定义格式如下:

def 方法名(self [, 形参列表]):
函数体

方法的调用格式如下: 对象.方法名([实参列表])
定义实例方法时,第一个参数必须为self。和前面一样,self指当前的实例对象。
调用实例方法时,不需要也不能给self传参。self由解释器自动传参

函数和方法的区别:

  • 都是用来完成一个功能的语句块,本质一样。
  • 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点
  • 直观上看,方法定义时需要传递self,函数不需要

实例对象的方法调用本质:

a.say_score(68) #Student.say_score(a,68)

其他用法:
dir(obj)可以获得对象的所有属性、方法
obj.dict 对象的属性字典
pass 空语句
isinstance(对象,类型) 判断“对象”是不是“指定类型”

6.类对象

解释器执行class语句时,就会创建一个类对象。
【操作】测试类对象的生成

class Student:
  pass  #空语句
​
print(type(Student))
print(id(Student))
​
Stu2 = Student
s1 = Stu2()
print(s1)

<class ‘type’>
51686328
<main.Student object at 0x0000000002B5FDD8>

7.类属性

类属性是从属于“类对象”的属性,也称为“类变量”。由于,类属性从属于类对象,可以被所有实例对象共享。
类属性的定义方式:

class 类名:
类变量名= 初始值

内存分析实例对象和类对象创建过程

class Student:
  company = "尚学堂" # 类属性
  count = 0 # 类属性
​
  def __init__(self, name, score):
    self.name = name # 实例属性
    self.score = score
    Student.count = Student.count + 1
​
  def say_score(self): # 实例方法
    print("我的公司是:", Student.company)
    print(self.name, '的分数是:', self.score)
​
s1 = Student('高淇', 80) # s1是实例对象,自动调用__init__()方法
s2 = Student('张三', 70)
s1.say_score()
print('一共创建{0}个Student对象'.format(Student.count))


从图中可以看出类属性是所有的实例方法都会存在的,调用实例属性时最后去执行类对象。

8.类方法_静态方法

类方法是从属于“类对象”的方法。类方法通过装饰器**@classmethod**来定义,格式如下:

@classmethod
def 类方法名(cls [,形参列表]) :
方法体

要点:

  • @classmethod必须位于方法上面一行
  • 第一个cls必须有;cls指的就是“类对象”本身
  • 调用类方法格式:类名.类方法名(参数列表)。 参数列表中,不需要也不能给cls传值
  • 类方法中访问实例属性和实例方法会导致错误
  • 子类继承父类方法时,传入cls是子类对象,而非父类对象(⚠️讲完继承再说)

【操作】类方法使用测试

class Student:
  company = "SXT"   #类属性
  
  @classmethod
  def printCompany(cls):
    print(cls.company)
  
Student.printCompany()

静态方法
Python中允许定义与“类对象”无关的方法,称为“静态方法”。“静态方法”和在模块中定义普通函数没有区别,只不过**“静态方法”放到了“类的名字空间里面”,需要通过“类调用”**。
静态方法通过装饰器@staticmethod来定义,格式如下:

@staticmethod
def 静态方法名([形参列表]) :
方法体

要点:

  • @staticmethod必须位于方法上面一行
  • 调用静态方法格式:类名.静态方法名(参数列表)
  • 静态方法中访问实例属性和实例方法会导致错误

【操作】静态方法使用测试

class Student:
  company = "SXT" # 类属性
​
  @staticmethod
  def add(a, b): # 静态方法
    print("{0}+{1}={2}".format(a,b,(a+b)))
    return a+b
Student.add(20,30)

9.del析构和垃圾回收机制

del()称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象占用的资源,例如:打开的文件资源、网络连接等。
Python实现自动的垃圾回收,当对象没有被引用时(引用计数为0),由垃圾回收器调用__del__()。
可以通过del语句删除对象,从而保证调用__del__()。
系统会自动提供__del__方法,一般不需要自定义析构方法。

#析构函数
class Person:
​
  def __del__(self):
    print("销毁对象:{0}".format(self))
​
p1 = Person()
p2 = Person()
del p2
print("程序结束")

销毁对象:<main.Person object at 0x02175610>
程序结束
销毁对象:<main.Person object at 0x021755D0>

10.call方法和可调用对象

Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象。
可调用对象包括自定义的函数、Python 内置函数、以及本节所讲的实例对象。
定义了__call__()的对象,称为“可调用对象”,即该对象可以像函数一样被调用。
该方法使得实例对象可以像调用普通函数那样,以“对象名()”的形式使用。

def f1():
  print("f1")
​
f1()  #本质也是调用了__call__()方法
​
class Car:
  def __call__(self, age,money):
    print("__call__方法")
    print("车龄:{0},金额:{1}".format(age,money))
​
f2 = Car()
f2(3,200000)  #像调用函数那样调用,本质也是调用了__call__()

f1
车龄:3,金额:200000

11.方法没有重载_方法的动态性

如果我们在类体中定义了多个重名的方法,只有最后一个方法有效。Python中,方法的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。因此,Python中是没有方法的重载的。

 #Python中没有方法的重载。定义多个同名方法,只有最后一个有效
 class Person:
 
   def say_hi(self):
     print("hello")
 
   def say_hi(self,name):
     print("{0},hello".format(name))
 
 p1 = Person()
 
 #p1.say_hi()    #不带参,报错:TypeError: say_hi() missing 1 required positional argument: 'name'
 
 p1.say_hi("高淇")

方法的动态性

#测试方法的动态性
class Person:
  def work(self):
    print("努力上班!")
​
def play_game(self):
  print("玩游戏")
​
def work2(s):
  print("好好工作,努力上班!")
​
Person.play = play_game
Person.work = work2
​
p = Person()
p.play()
p.work()

Person动态的新增了play_game方法,以及用work2替换了work方法

12.私有属性和私有方法

  • 通常我们约定,两个下划线开头的属性是私有的(private)。其他为公共的(public)。
  • 类内部可以访问私有属性(方法)
  • 类外部不能直接访问私有属性(方法)
  • 类外部可以通过**_类名__私有属性(方法)名**”访问私有属性(方法)
class Employee:
  __company = "4"

print(Employee.__company) #AttributeError: type object 'Employee' has no attribute '__company'

【测试】私有属性和公有属性使用测试

#测试私有属性、私有方法
class Employee:
  __company = "百战程序员" #私有.通过dir查到_Employee__company
​
  def __init__(self,name,age):
    self.name = name
    self.__age = age     #私有实例属性
​
  def say_company(self):
    print("我的公司是:",Employee.__company)     #类内部可以直接访问私有属性
    print(self.name,"的年龄是:",self.__age)
    self.__work()
​
  def __work(self): #私有实例方法,通过dir可查到_Employee__work
    print("工作!好好工作,好好赚钱,娶个媳妇!")
​
p1 = Employee("高淇",32)
print(p1.name)
print(dir(p1))       #
p1.say_company()
print(p1._Employee__age)   #通过这种方式可以直接访问到私有属性  。通过dir可以查到属性:_Employee__age
#print(p1.__age)      #直接访问私有属性,报错
#p1.__sleep()       #直接访问私有方法,报错

高淇
[‘_Employee__age’, ‘_Employee__company’, ‘_Employee__work’, ‘class’, ‘delattr’, ‘dict’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘name’, ‘say_company’]
我的公司是: 百战程序员
高淇 的年龄是: 32
工作!好好工作,好好赚钱,娶个媳妇!
32

13.@property装饰器

@property可以将一个方法的调用方式变成“属性调用”。
@property主要用于帮助我们处理属性的读操作、写操作。对于某一个属性,我们可以直接通过:
emp1.salary = 30000
如上的操作读操作、写操作。但是,这种做法不安全。比如,我需要限制薪水必须为1-10000的数字。这时候就需要通过使用装饰器@property来处理。

#测试@property
class Employee:
​
  def __init__(self,name,salary):
    self.name = name
    self.__salary = salary
​
  @property        #相当于salary属性的getter方法
  def salary(self):
    print("月薪为{0},年薪为{1}".format(self.__salary,(12*self.__salary)))
    return self.__salary;
​
  @salary.setter
  def salary(self,salary):  #相当于salary属性的setter方法
    if(0<salary<1000000):
      self.__salary = salary
    else:
      print("薪水录入错误!只能在0-1000000之间")
​
emp1 = Employee("高淇",100)
print(emp1.salary)
​
emp1.salary = -200

月薪为100,年薪为1200
100
月薪为100,年薪为1200
100
薪水录入错误!只能在0-1000000之间

14.属性和类的命名规则总结

xxx:保护成员,不能用from module import *导入,只有类对象和子类对象能访问这些成员。(⚠️讲完模块再看)
xxx系统定义的特殊成员,比如__init()
_
__xxx: 类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但,在类外部可以通过对象名. _类名__xxx这种特殊方式访问。Python不存在严格意义的私有成员)
类代码风格:
类名首字母大写,多个单词之间采用驼峰原则。
实例名、模块名采用小写,多个单词之间采用下划线隔开
每个类,应紧跟“文档字符串”,说明这个类的作用
可以用空行组织代码,但不能滥用。在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类

15.None对象的特殊性

与C和JAVA不同,python中是没有NULL的,取而代之的是None。
None是一个特殊的常量,表示变量没有指向任何对象。
在Python中,None本身实际上也是对象,有自己的类型NoneType。
你可以将None赋值给任何变量,但我们不能创建NoneType类型的对象。
None不是False,None不是0,None不是空字符串。None和任何其他的数据类型比较永远返回False。

None和其他类型的比较
None和其他任何类型比较都会返回False:

a = None
if a is None and a==None:
  print("a是None")       #会执行
if a==False or a==0:
  print("None不等于False")    #不会被打印

空列表、空字符串、0之间的比较:
if语句判断时,空列表[]、空字典{}、空元组()、0等一系列代表空和无的对象会被转换成False:

a=[];b=();c={};d="";e=0;f=None
if (not a) and (not b) and (not c) and (not d) and (not e) and (not f):
  print("if判断时,空列表[]、空字符串、0、None等代表空和无的对象会被转换成False")

==和is判断时,空列表、空字符串不会自动转成False:

a=[];b=();c={};d="";e=0;
if (a==False or d==False):
  print("==时,空列表、空字符串不是False!")  #不会执行
if(e==False):
  print("==时,0会转成False")

16.面向对象的三大特征说明(封装、继承、多态)

Python是面向对象的语言,支持面向对象编程的三大特性:继承、封装(隐藏)、多态。
封装(隐藏)
隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。
通过前面学习的“私有属性、私有方法”的方式,实现“封装”。Python追求简洁的语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。
继承
继承可以让子类具有父类的特性,提高了代码的重用性。
从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。
多态
多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是“敲几行代码”。

16.1 继承

继承是面向对象编程的三大特征之一。继承让我们更加容易实现类的扩展。实现代码的重用,不用再重新发明轮子(don’t reinvent wheels)。
如果一个新类继承自一个设计好的类,就直接具备了已有类的特征,就大大降低了工作难度。已有的类,我们称为“父类或者基类”,新的类,我们称为“子类或者派生类”。

Python支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:

class 子类类名(父类1[,父类2,…]):
类体

class Car:  #父类是object
  pass
class Benz(Car):  #Benz----Car----object
  pass

如果在类定义中没有指定父类,则默认父类是object类。也就是说,object是所有类的父类,里面定义了一些所有类共有的默认实现,比如:new()
关于构造函数:
子类不重写 init,实例化子类时,会自动调用父类定义的 init
子类重写了__init__ 时,实例化子类,就不会调用父类已经定义的 init
如果重写了__init__ 时,要使用父类的构造方法,可以使用 super 关键字,也可以使用如下格式调用:父类名.init(self, 参数列表)

class Person:
  def __init__(self,name,age):
    print("Person的构造方法")
    self.name = name
    self.age = age
​
  def say_age(self):
    print(self.name,"的年龄是:",self.age)

class Student(Person): #继承了Person的属性和方法
​
  def __init__(self,name,age,score):
    # 子类并不会自动调用父类的__init__(),我们必须显式的调用它。
    # Person.__init__(self, name, age)
    # super(Student,self).__init__(name,age)
​
    print("Student的构造方法")
    # self.name = name
    # self.age = age
    self.score = score
​
s1 = Student("张三",15,85)
#s1.say_age()
print(dir(s1))

16.2 继承和方法重写

成员继承:子类继承了父类除构造方法之外的所有成员。
⚠️(私有属性、私有方法也被继承)
方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”

class Person:
  def __init__(self,name,age):
    self.name = name
    self.age = age
​
  def say_age(self):
    print(self.name,"的年龄是:",self.age)
​
  def say_name(self):
    print("我是",self.name)
​
​
class Student(Person):
​
  def __init__(self,name,age,score):
    Person.__init__(self,name,age) 
    self.score = score
​
  def say_score(self):
    print(self.name,"的分数是:",self.score)
​
  def say_name(self):   #重写父类的方法
    print("报告老师,我是",self.name)
​
s1 = Student("张三",15,85)
s1.say_score()
s1.say_name()
s1.say_age()

张三 的分数是: 85
报告老师,我是 张三
张三 的年龄是: 15

查看类的继承层次结构
通过类的方法mro()或者类的属性__mro__可以输出这个类的继承层次结构。

class A:pass
class B(A):pass
class C(B):pass
​
print(C.mro())

16.3 object根类

object类是所有类的父类,因此所有的类都有object类的属性和方法。
alt+7 或者左栏可以查看当前程序的结构:

在这里插入图片描述

dir()查看对象属性
为了深入学习对象,内置函数dir(),可以看到指定对象所有的属性:

class Person:
  def __init__(self,name,age):
    self.name = name
    self.age = age
​
  def say_age(self):
    print(self.name,"的年龄是:",self.age)
​
obj = object()
print(dir(obj))
​
s2 = Person("高淇",18)
print(dir(s2))

[‘class’, ‘delattr’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’]

[‘class’, ‘delattr’, ‘dict’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘age’, ‘name’, ‘say_age’]

Person对象增加了六个属性:
dictmoduleweakref、 age 、name 、say_age
object的所有属性,Person类作为object的子类,显然包含了所有的属性
打印age、name、say_age,发现say_age虽然是方法,实际上也是属性。只不过,这个属性的类型是method而已。

age <class ‘int’>
name <class ‘str’>
say_age <class ‘method’>

16.4 重写str

  • object有一个__str__()方法,用于返回一个对于“对象的描述”。内置函数str(对象),调用的就是__str__()
  • str()经常用于print()方法,帮助我们查看对象的信息。str()可以重写
class Person:
  def __init__(self,name,age):
    self.name = name
    self.__age = age
​
  def __str__(self):
    '''将对象转化成一个字符串,一般用于print方法'''
    print("重写__str__方法")
    return "名字是:{0},年龄是{1}".format(self.name,self.__age)
​
p = Person("高淇",18)
print(p)
s = str(p)

16.5 多重继承


Python支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父类”的特点。但是由于,这样会被“类的整体层次”搞的异常复杂,尽量避免使用。

class A:
  def aa(self):
    print("aa")
​
class B:
  def bb(self):
    print("bb")
​
class C(B,A):
  def cc(self):
    print("cc")
​
c = C()
c.cc()
c.bb()
c.aa()

在这里插入图片描述

16.6 MEO方法解析顺序

Python支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将“从左向右”按顺序搜索。
MRO(Method Resolution Order):方法解析顺序。 我们可以通过mro()方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。

class A:
  def aa(self):
    print("aa")
​
  def say(self):
    print("say AAA!")
​
class B:
  def bb(self):
    print("bb")
​
  def say(self):
    print("say BBB!")
class C(B,A):
  def cc(self):
    print("cc")
​
c = C()
print(C.mro())     #打印类的层次结构
c.say()         #解释器寻找方法是“从左到右”的方式寻找,此时会执行B类中的say()

[main.C’>, main.B’>, main.A’>, ] say BBB!

16.7 super()获得父类

在子类中,如果想要获得父类的方法时,我们可以通过super()来做。
super()代表父类的定义,不是父类对象。
调用父类的构造方法:super(子类名称,self).init(参数列表)

class A:
  def __init__(self):
    print("A的构造方法")
​
  def say(self):
    print("A: ",self)
    print("say AAA")
​
class B(A):
  def __init__(self):
    super(B,self).__init__() #调用父类的构造方法
    print("B的构造方法")
  def say(self):
    #A.say(self)   调用父类的say方法
    super().say()  #通过super()调用父类的方法
    print("say BBB")
​
b = B()
b.say()

A: <main.B object at 0x007A5690>
say AAA
say BBB

16.8 多态详解

多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。比如现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。

  • 多态是方法的多态,属性没有多态。
  • 多态的存在有2个必要条件:继承、方法重写
#多态
class Animal:
  def shout(self):
    print("动物叫了一声")

class Dog(Animal):
  def shout(self):
    print("小狗,汪汪汪")
​
class Cat(Animal):
​
  def shout(self):
    print("小猫,喵喵喵")
​
def animalShout(a):
  a.shout()  #传入的对象不同,shout方法对应的实际行为也不同。
​
animalShout(Dog())
animalShout(Cat())

16.9 特殊方法和运算符重载

Python的运算符实际上是通过调用对象的特殊方法实现的

a = 20
b = 30
c = a+b
d = a.__add__(b)
print("c=",c)
print("d=",d)

c= 50
d= 50

常见的特殊方法统计如下:
在这里插入图片描述
每个运算符实际上都对应了相应的方法:
在这里插入图片描述
实现了“运算符的重载”:

#测试运算符的重载
​
class Person:
  def __init__(self,name):
    self.name = name
​
  def __add__(self, other):
    if isinstance(other,Person):
      return "{0}--{1}".format(self.name,other.name)
    else:
      return "不是同类对象,不能相加"
​
  def __mul__(self, other):
    if isinstance(other,int):
      return self.name*other
    else:
      return "不是同类对象,不能相乘"
​
p1 = Person("高淇")
p2 = Person("高希希")
​
x = p1 + p2
print(x)
print(p1*3)

16.10 特殊属性

Python对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。
在这里插入图片描述

#测试特殊属性
class A:
  pass
​
class B:
  pass
​
class C(B,A):
​
  def __init__(self,nn):
    self.nn = nn
​
  def cc(self):
    print("cc")
​
c = C(3)
​
print(c.__dict__)
print(c.__class__)
print(C.__bases__)
print(C.mro())
print(A.__subclasses__())

[‘class’, ‘delattr’, ‘dict’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘cc’, ‘nn’]
{‘nn’: 3}
<class ‘main.C’>
(<class ‘main.B’>, <class ‘main.A’>)
[<class ‘main.C’>, <class ‘main.B’>, <class ‘main.A’>, <class ‘object’>]
[<class ‘main.C’>]

16.11 对象的浅拷贝和深拷贝

在这里插入图片描述
浅拷贝
Python拷贝一般都是浅拷贝。
浅拷贝:拷贝时,拷贝源对象,但对象包含的子对象内容不拷贝

深拷贝
使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象。
深拷贝:拷贝时,拷贝源对象,也递归拷贝对象中包含的子对象

#测试对象的引用赋值、浅拷贝、深拷贝
import copy
​
class MobilePhone:
  def __init__(self,cpu):
    self.cpu = cpu
​
class CPU:
  pass
​
c = CPU()
m = MobilePhone(c)
​
print("----浅拷贝-------")
m2 = copy.copy(m)  #m2是新拷贝的另一个手机对象
print("m:",id(m))
print("m2:",id(m2))
print("m的cpu:",id(m.cpu))
print("m2的cpu:",id(m2.cpu))  #m2和m拥有了一样的cpu对象
print("----深拷贝--------")
m3 = copy.deepcopy(m)
print("m:",id(m))
print("m3:",id(m3))
print("m的cpu:",id(m.cpu))
print("m3的cpu:",id(m3.cpu))  #m3和m拥有不一样的cpu对象

----浅拷贝-------
m: 1879267229360
m2: 1879267228592
m的cpu: 1879267229648
m2的cpu: 1879267229648
----深拷贝--------
m: 1879267229360
m3: 1879267222256
m的cpu: 1879267229648
m3的cpu: 1879267221968

16.12 继承和组合

除了继承,“组合”也能实现代码的复用,“组合”核心是“将父类对象作为子类的属性”。

  • is-a关系,可以使用“继承”。从而实现子类拥有的父类的方法和属性。is-a关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。
  • has-a关系,可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。has-a关系指的是这样的关系:手机拥有CPU。MobilePhone has a CPU
#组合测试
class MobilePhone:
  def __init__(self,cpu,screen):
    self.cpu = cpu
    self.screen = screen
​
class CPU:
​
  def calculate(self):
    print("计算,算个12345")
​
class Screen:
  def show(self):
    print("显示一个好看的画面,亮瞎你的钛合金大眼")
#​class MobilePhone(CPU,MobilePhone):
	#pass
c = CPU()
s = Screen()
m = MobilePhone(c,s)
m.cpu.calculate()    #通过组合,我们也能调用cpu对象的方法。相当于手机对象间接拥有了“cpu的方法”
m.screen.show()

计算,算个12345
显示一个好看的画面,亮瞎你的钛合金大眼

17.设计模式-工厂模式

设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法,工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。

#工厂模式
class CarFactory:
  def createCar(self,brand):
    if brand == "奔驰":
      return Benz()
    elif brand == "宝马":
      return BMW()
    elif brand == '比亚迪':
      return BYD()
    else:
      return "未知品牌,无法创建"
​
class Benz:
  pass
​
class BMW:
  pass
​
class BYD:
  pass
​
factory = CarFactory()
c1 = factory.createCar("奔驰")
c2 = factory.createCar("宝马")
print(c1)
print(c2)

<main.Benz object at 0x021C5770>
<main.BMW object at 0x021C5790>

18.设计模式-单例模式

单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。
单例模式有多种实现的方式,我们这里推荐重写__new__()的方法。

#单例模式

class MySingleton:
  __obj = None
  __init_flag = True
​
  def __new__(cls, *args, **kwargs):
    if cls.__obj == None:
      cls.__obj = object.__new__(cls)return cls.__obj
​
  def __init__(self,name):
    if MySingleton.__init_flag:
      print("init....")
      self.name = name
      MySingleton.__init_flag = False
​
a = MySingleton("aa")
print(a)
b = MySingleton("bb")
print(b)

init…
<main.MySingleton object at 0x01E15610>
<main.MySingleton object at 0x01E15610>

19.工厂和单例模式结合

#测试工厂模式和单例模式的整合使用
class CarFactory:
  __obj = None      #类属性
  __init_flag = True
​
  def create_car(self,brand):
    if brand =="奔驰":
      return Benz()
    elif brand =="宝马":
      return BMW()
    elif brand == "比亚迪":
      return BYD()
    else:
      return "未知品牌,无法创建"
​
  def __new__(cls, *args, **kwargs):
    if cls.__obj ==None:
      cls.__obj = object.__new__(cls)

    return cls.__obj
​
  def __init__(self):
    if CarFactory.__init_flag:
      print("init CarFactory....")
      CarFactory.__init_flag = False
​
class Benz:
  pass
​
class BMW:
  pass
​
class BYD:
  pass
​
factory = CarFactory()
c1 = factory.create_car("奔驰")
c2 = factory.create_car("比亚迪")
print(c1)
print(c2)
​
factory2 = CarFactory()
print(factory)
print(factory2)

init CarFactory…
<main.Benz object at 0x01E36E90>
<main.BYD object at 0x01E36C30>
<main.CarFactory object at 0x01E36730>
<main.CarFactory object at 0x01E36730>

20.练习

20.1定义发动机类Motor、底盘类Chassis、座椅类Seat,车辆外壳类Shell,并使用组合关系定义汽车类。

其他要求:定义汽车的run()方法,里面需要调用Motor类的work()方法,也需要调用座椅类Seat的work()方法,也需要调用底盘类Chassis的work()方法

20.2使用工厂模式、单例模式实现需求:

(1) 电脑工厂类ComputerFactory用于生产电脑Computer。工厂类使用单例模式,也就是说只能有一个工厂对象。
(2) 工厂类中可以生产各种品牌的电脑:联想、华硕、神舟
(3) 各种品牌的电脑使用继承实现:
(4) 父类是Computer类,定义了calculate方法
(5) 各品牌电脑类需要重写父类的calculate方法

20.3定义一个Employee雇员类要求:

(1) 属性有:id、name、salary
(2) 运算符重载+:实现两个对象相加时,默认返回他们的薪水和
(3) 构造方法要求:输入name、salary,不输入id。id采用自增的方式,从1000开始自增,第一个新增对象是1001,第二个新增对象是1002。
(4) 根据salary属性,使用@property设置属性的get和set方法。set方法要求输入:1000-50000范围的数字。

二、Python开发环境搭建

1.PyCharm下搭建

2.VSCode下搭建

3.虚拟环境使用

3.1 介绍

Python语言的时候使用pip来安装第三方包,但是由于pip的特性,系统中只能安装每个包的一个版本。但是在实际项目开发中,不同项目可能需要第三方包的不同版本,Python的解决方案就是虚拟环境。虚拟环境就是虚拟出来的一个隔离的Python环境,每个项目都可以有自己的虚拟环境,用pip安装各自的第三方包,不同项目之间也不会存在冲突。
虚拟环境安装

pip install virtualenv

安装过程中,如果出现如下 error 信息是,可以尝试切换源重新下载:

pip install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple/

虚拟环境的使用:
创建虚拟环境:

virtualenv --system-site-packages 虚拟环境的名字 #这个会继承你本地的环境的所有的安装的第三方模块
virtualenv -p /usr/bin/python3.7 venv

设置存放虚拟环境的盘
进入环境包的Scripts中激活:
在这里插入图片描述

切换虚拟环境:

source venv/bin/activate

退出虚拟环境:

. venv/bin/deactivate

删除虚拟环境:
要删除一个虚拟环境,只需删除它的文件夹: rd /s filename

3.2 pycharm

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

3.3 vscode

直接点击右下角的虚拟环境可进行切换
在这里插入图片描述在这里插入图片描述

4.管理工具

virtualenvwrapper提供了一系列命令使得和虚拟环境工作变得便利。它把你所有的虚拟环境都放在一个地方
Linux、Unix、MacOS: pip install virtualenvwrapper
Windows: pip install virtualenvwrapper-win

注意:默认虚拟环境会安装到用户的家目录
解决方案:设置WORKON_HOME到环境变量中,即可解决

在环境变量中,配置虚拟环境的指定安装目录:
打开系统环境变量配置
新建系统变量名:WORKON_HOME
变量值配置为你的系统中一个指定目录,譬如:F:\python3_env

在这里插入图片描述

创建虚拟环境

mkvirtualenv env_name

在这里插入图片描述

激活虚拟环境

workon env_name

退出虚拟环境

deactivate

删除虚拟环境

rmvirtualenv env_name

三、异常处理

1.异常本质

伪代码说明异常机制
如果要拷贝一个文件,在没有异常机制的情况下,需要考虑各种异常情况,伪代码如下:
在这里插入图片描述
这种方式,有两个坏处:
逻辑代码和错误处理代码放一起;
程序员本身需要考虑的例外情况较复杂,对程序员本身要求较高。
如上情况,如果是用Python的异常机制来处理,对比如下:
在这里插入图片描述
异常机制本质:当程序出现异常,程序安全的退出、处理完后继续执行的机制

python中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。下面较为完整的展示了python中内建异常类的继承层次:
在这里插入图片描述
python中一切都是对象,异常也采用对象的方式来处理。处理过程:
抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给解释器。
捕获异常:解释器得到该异常后,寻找相应的代码来处理该异常

2.Try…except

2.1 基本结构

单个结构:

try:
被监控的可能引发异常的语句块
except BaseException [as e]:
异常处理语句块

try块包含着可能引发异常的代码,except块则用来捕捉和处理发生的异常。
执行的时候,如果try块中没有引发异常,则跳过ecept块继续执行后续代码;
执行的时候,如果try块中发生了异常,则跳过try块中的后续代码,跳到相应的except块中处理异常;异常处理完后,继续执行后续代码。

#测试简单的0不能做除数异常
try:
  print("step1")
  a = 3/0
  print("step2")
except BaseException as e:
  print("step3")
  print(e)
​
print("step4")

step1
step3
division by zero
step4

在这里插入图片描述

step1
step2
step4
在这里插入图片描述

【示例】循环输入数字,如果不是数字则处理异常;直到输入88,则结束循环。

while True:
  try:
    x = int(input("请输入一个数字:"))
    print("您入的数字是",x)
    if x==88:
      print("退出程序")
      break
  except:
    print("异常:输入的不是一个数字")

2.2 多个except结构

建议尽量捕获可能出现的多个异常(按照先子类后父类的顺序),并且针对性的写出异常处理代码。

try:
    被监控的、可能引发异常的语句块
except Exception1:
    处理Exception1的语句块
except Exception2:
    处理Exception2的语句块
​
[...]
except BaseException:
    处理可能遗漏的异常的语句块

【示例】多个except结构

try:
  a = input("请输入被除数:")
  b = input("请输入除数:")
  c = float(a)/float(b)
  print(c)
​
except ZeroDivisionError:
  print("异常:除数不能为0")
except TypeError:
  print("异常:除数和被除数都应该为数值类型")
except BaseException as e:
  print(e)
  print(type(e))

3.try…except…else结构

try…except…else结构增加了else块。如果try块中没有抛出异常,则执行else块。如果try块中抛出异常,则执行except块,不执行else块。

try:
  a = input("请输入被除数:")
  b = input("请输入除数:")
  c = float(a)/float(b)
except BaseException as e:
  print(e)
else:
  print("除的结果是:",c)

发生异常的执行情况(执行except块,没有执行else):

请输入被除数:5
请输入除数:0
float division by zero

没有发生异常的执行情况(执行完try块后,执行else):

请输入被除数:10
请输入除数:5
除的结果是: 2.0

4.finally和return

try…except…finally结构:finally块无论是否发生异常都会被执行;通常用来释放try块中申请的资源。

try:
  a = input("请输入一个被除数:")
  b = input("请输入一个除数:")
  c = float(a)/float(b)
except BaseException as e:
  print(e)
else:
  print(c)
finally:
  print("我是finally中的语句,无论发生异常与否,都执行!")
​
print("程序结束!")

请输入被除数:10
请输入除数:0
float division by zero
我是finally中的语句,无论是否发生异常都执行

【示例】读取文件,finally中保证关闭文件资源

try:
  f = open("d:/a.txt",'r')
  content = f.readline()
  print(content)
except BaseException as e:
  print(e)
finally:
  f.close()    #释放资源。此处也可能会发生异常。若发生异常,则程序终止,不会继续往下执行
​
print("step4")

Traceback (most recent call last):
[Errno 2] No such file or directory: ‘d:/a.txt’
File “…mypro_exception/my01.py”, line 8, in
f.close() #释放资源。此处也可能会发生异常。若发生异常,则程序终止,不会继续往下执行
NameError: name ‘f’ is not defined

Process finished with exit code 1

return语句和异常处理问题
由于return有两种作用:结束方法运行、返回值。我们一般不把return放到异常处理结构中,而是放到方法最后。

def test01():
  print("step1")
  try:
    x = 3/0
    # return "a"
  except:
    print("step2")
    print("异常:0不能做除数")
    #return "b"
  finally:
    print("step4")
    #return "d"
​
  print("step5")
  return "e"     #一般不要将return语句放到try、except、else、finally块中,会发生一些意想不到的错误。建议放到方法最后。
print(test01())

5.常见异常汇总

常见异常的解决:
SyntaxError:语法错误

int a =3
^
SyntaxError: invalid syntax

NameError:尝试访问一个没有申明的变量

print(a)
NameError: name ‘a’ is not defined

ZeroDivisionError:除数为0错误(零除错误)

a = 3/0
ZeroDivisionError: division by zero

ValueError:数值错误

float(“gaoqi”)
ValueError: could not convert string to float: ‘gaoqi’

TypeError:类型错误

123+“abc”
TypeError: unsupported operand type(s) for +: ‘int’ and ‘str’

AttributeError:访问对象的不存在的属性

a=100
a.sayhi()
AttributeError: ‘int’ object has no attribute ‘sayhi’

IndexError:索引越界异常

a = [4,5,6]
a[10]
IndexError: list index out of range

KeyError:字典的关键字不存在

a = {‘name’:“gaoqi”,‘age’:18}
a[‘salary’]
KeyError: ‘salary’

常见异常汇总:

异常名称说明
ArithmeticError所有数值计算错误的基类
AssertionError断言语句失败
AttributeError对象没有这个属性
BaseException所有异常的基类
DeprecationWarning关于被弃用的特征的警告
EnvironmentError操作系统错误的基类
EOFError没有内建输入,到达EOF 标记
Exception常规错误的基类
FloatingPointError浮点计算错误
FutureWarning关于构造将来语义会有改变的警告
GeneratorExit生成器(generator)发生异常来通知退出
ImportError导入模块/对象失败
IndentationError缩进错误
IndexError序列中没有此索引(index)
IOError输入/输出操作失败
KeyboardInterrupt用户中断执行(通常是输入^C)
KeyError映射中没有这个键
LookupError无效数据查询的基类
MemoryError内存溢出错误(对于Python 解释器不是致命的)
NameError未声明/初始化对象 (没有属性)
NotImplementedError尚未实现的方法
OSError操作系统错误
OverflowError数值运算超出最大限制
OverflowWarning旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning关于特性将会被废弃的警告
ReferenceError弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError一般的运行时错误
RuntimeWarning可疑的运行时行为(runtime behavior)的警告
StandardError所有的内建标准异常的基类
StopIteration迭代器没有更多的值
SyntaxError Python语法错误
SyntaxWarning可疑的语法的警告
SystemError一般的解释器系统错误
SystemExit解释器请求退出
TabErrorTab 和空格混用
TypeError对类型无效的操作
UnboundLocalError访问未初始化的本地变量
UnicodeDecodeErrorUnicode 解码时的错误
UnicodeEncodeErrorUnicode 编码时错误
UnicodeErrorUnicode 相关的错误
UnicodeTranslateErrorUnicode 转换时错误
UserWarning用户代码生成的警告
ValueError传入无效的参数
Warning警告的基类
WindowsError系统调用失败
ZeroDivisionError除(或取模)零 (所有数据类型)

6.with上下文管理资源

finally块由于是否发生异常都会执行,通常我们放释放资源的代码。可以通过with上下文管理,更方便的实现释放资源的操作。

with context_expr [ as var]:
语句块

with上下文管理可以自动管理资源,在with代码块执行完毕后自动还原进入该代码之前的现场或上下文。不论何种原因跳出with块,不论是否有异常,总能保证资源正常释放。极大的简化了工作,在文件操作、网络通信相关的场合非常常用。

with open("d:/bb.txt") as f:
  #content = f.readline()
  #print(content)
  for line in f:
    print(line)

7.traceback的使用_异常写入日志文件

【示例】使用traceback将异常信息写入日志文件

#coding=utf-8
import traceback
​
try:
  print("step1")
  num = 1/0
except:
  with open("d:/a.log","a") as f:
    traceback.print_exc(file=f)

8.自定义异常

程序开发中,有时候需要自己定义异常类。自定义异常类一般都是运行时异常,通常继承Exception或其子类即可。命名一般以Error、Exception为后缀。自定义异常由raise语句主动抛出。

#测试自定义异常类
​
class AgeError(Exception): #继承Exception
  def __init__(self,errorInfo):
    Exception.__init__(self)
    self.errorInfo = errorInfo
  def __str__(self):
    return str(self.errorInfo)+",年龄错误!应该在1-150之间"
############测试代码################
if __name__ == "__main__":  #如果为True,则模块是作为独立文件运行,可以执行测试代码
  age = int(input("输入一个年龄:"))
  if age<1 or age>150:
    raise AgeError(age)
  else:
    print("正常的年龄:",age)

输入一个年龄:200
Traceback (most recent call last):
File “…mypro_exception/my10.py”, line 16, in
raise AgeError(age)
main.AgeError: 200,年龄错误!应该在1-150之间

9.pycharm调试(debug)

进行调试的核心是设置断点。程序执行到断点时,暂时挂起,停止执行。就像看视频按下停止一样,我们可以详细的观看停止处的每一个细节。

设置断点:在行号后面单击即可增加断点。在断点上再单击即可取消断点后进入调试视图

在这里插入图片描述

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

四、文件处理

1.简介与写入

一个完整的程序一般都包括数据的存储和读取;在前面写的程序数据都没有进行实际的存储,因此python解释器执行完数据就消失了。实际经常需要从外部存储介质(硬盘、光盘、U盘等)读取数据,或者将程序产生的数据存储到文件中,实现“持久化”保存。
文本文件和二进制文件
文本文件:文本文件存储的是普通“字符”文本,python默认为unicode字符集(两个字节表示一个字符,最多可以表示:65536个),可以使用记事本程序打开。
二进制文件:二进制文件把数据内容用“字节”进行存储,无法用记事本打开。必须使用专用的软件解码。常见的有:MP4视频文件、MP3音频文件、JPG图片、doc文档等等。

文件操作相关模块:
在这里插入图片描述
创建文件对象open()
open()函数用于创建文件对象,基本语法格式如下:
open(文件名[,打开方式])
如果只是文件名,代表在当前目录下的文件。文件名可以录入全路径,比如:D:\a\b.txt
为了减少\的输入,可以使用原始字符串:r“d:\b.txt” r可以将所有\表示为路径,示例如下:f = open(r"d:\b.txt",“w”)
在这里插入图片描述
文本文件对象和二进制文件对象的创建:
如果没有增加模式b,则默认创建的是文本文件对象,处理的基本单元是“字符”。
如果是二进制模式b,则创建的是二进制文件对象,处理的基本单元是“字节”。

文本文件的写入
文本文件的写入一般就是三个步骤:创建文件对象、写入数据、关闭文件对象。
【操作】文本写入操作简单测试

f = open(r"a.txt","a")
s = "itbaizhan\nsxt\n"
f.write(s)
f.close()
with open(r"a.txt","a") as f:
  s = "itbaizhan\nsxt\n"
  f.write(s)

2.常用编码介绍

ASCII
全称为American Standard Code for Information Interchange,美国信息交换标准代码,这是世界上最早最通用的单字节编码系统,主要用来显示现代英语及其他西欧语言。
ASCII码用7位表示,只能表示128个字符。只定义了27=128个字符,用7bit即可完全编码,而一字节8bit的容量是256,所以一字节ASCII的编码最高位总是0。
0~31表示控制字符如回车、退格、删除等;32~126表示打印字符即可以通过键盘输入并且能显示出来的字符; 其中48~57为0到9十个阿拉伯数字,65~90为26个大写英文字母,97~122号为26个小写英文字母,其余为一些标点符号、运算符号等,具体可以参考ASCII标准表(大家自行百度,不在此赘述)。

GBK
全称为Chinese Internal Code Specification,即汉字内码扩展规范,于1995年制定。它主要是扩展了GB2312,在它的基础上又加了更多的汉字,它一共收录了21003个汉字。

Unicode
Unicode编码设计成了固定两个字节,所有的字符都用16位(2^16=65536)表示,包括之前只占8位的英文字符等,所以会造成空间的浪费,UNICODE在很长的一段时间内都没有得到推广应用。
Unicode完全重新设计,不兼容iso8859-1,也不兼容任何其他编码。

UTF-8
对于英文字母,unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了UTF编码,UTF-8全称是(8-bit Unicode Transformation Format)。
UTF编码兼容iso8859-1编码,同时也可以用来表示所有语言的字符,不过,UTF编码是不定长编码,每一个字符的长度从1-4个字节不等。其中,英文字母都是用一个字节表示,而汉字使用三个字节。

中文乱码问题
windows操作系统默认的编码是GBK,Linux操作系统默认的编码是UTF-8。当我们用open()时,调用的是操作系统打开的文件,默认的编码是GBK。

#测试写入中文
f = open(r"b.txt","w")
f.write("尚学堂\n百战程序员\n")
f.close()

在文件编辑区单击右键,选择FileEncoding,选择GBK即可:
在这里插入图片描述
【示例】通过指定文件编码解决中文乱码问题

#测试写入中文
f = open(r"b.txt","w",encoding="utf-8")
f.write("尚学堂\n百战程序员\n")
f.close()

write()/writelines()写入数据
write(a):把字符串a写入到文件中
writelines(b):把字符串列表写入文件中,不添加换行符
【操作】添加字符串列表数据到文件中

f = open(r"d:\bb.txt","w",encoding="utf-8")
s = ["高淇\n","高老三\n","高老四\n"]
f.writelines(s)
f.close()

3.关闭流要点

3.1finally异常管理

由于文件底层是由操作系统控制,所以我们打开的文件对象必须显式调用close()方法关闭文件对象。当调用close()方法时,首先会把缓冲区数据写入文件(也可以直接调用flush()方法),再关闭文件,释放文件对象。
为了确保打开的文件对象正常关闭,一般结合异常机制的finally或者with关键字实现无论何种情况都能关闭打开的文件对象。
【操作】结合异常机制的finally,确保关闭文件对象

try:
  f = open(r"my01.txt","a")
  s = "gaoqi"
  f.write(s)
except BaseException as e:
  print(e)
finally:
  f.close()

3.2with上下文管理

with关键字(上下文管理器)可以自动管理上下文资源,不论什么原因跳出with块,都能确保文件正确的关闭,并且可以在代码块执行完毕后自动还原进入该代码块时的现场。

s = ["高淇\n","高老三\n","高老五\n"]
with open(r"d:\bb.txt","w") as f:
  f.writelines(s)

4.文本文件的读取

read([size]) :从文件中读取size个字符,并作为结果返回。如果没有size参数,则读取整个文件。 读取到文件末尾,会返回空字符串。
readline():读取一行内容作为结果返回。读取到文件末尾,会返回空字符串
readlines():文本文件中,每一行作为一个字符串存入列表中,返回该列表

with open("b.txt","r",encoding="gbk") as f:
    s = f.read()
    s2 = f.read()
    print(s)
    print("第二次读的:",s2) #第一次把所有字符都取完了,所以第二次读取是空的

尚学堂
百战程序员
第二次读的:

【操作】 读取一个文件前4个字符

with open(r"a.txt","r",encoding="utf-8") as f:
  print(f.read(4))

【操作】文件较小,一次将文件内容读入到程序中

with open(r"d:\bb.txt","r") as f:
  print(f.read())

【操作】按行读取一个文件

with open(r"bb.txt","r") as f:
  while True:
    fragment = f.readline()
    if not fragment: #如果非空
      break
    else:
      print(fragment,end="")

【操作】使用迭代器(每次返回一行)读取文本文件

with open(r"d:\bb.txt","r") as f:
  for a in f:
    print(a,end="")

【操作】为文本文件每一行的末尾增加行号
把每行内容读取出来后再增加行号,增加索引循环为行号遍历lines,

with open("b.txt","r",encoding="utf-8") as f:
  lines = f.readlines()
  lines2 = [line.rstrip()+"  #"+str(index)+"\n" for index,line in zip(range(1,len(lines)+1),lines)] #line.rstrip()去掉右侧空白符
with open("b.txt","w",encoding="utf-8") as f:
  f.writelines(lines2)

执行前文件内容:

我love u! 尚学堂 百战程序员

执行程序后文件内容:

我love u! #1 尚学堂 #2 百战程序员 #3

5.二进制文件的读写

二进制文件的处理流程和文本文件流程一致。首先还是要创建文件对象,不过,我们需要指定二进制模式,从而创建出二进制文件对象。例如:
f = open(r"d:\a.txt", ‘wb’) #可写的、重写模式的二进制文件对象
f = open(r"d:\a.txt", ‘ab’) #可写的、追加模式的二进制文件对象
f = open(r"d:\a.txt", ‘rb’) #可读的二进制文件对象

创建好二进制文件对象后,仍然可以使用write()、read()实现文件的读写操作。
【操作】 读取图片文件,实现文件的拷贝

with open('logo1.png', 'rb')as srcFile,open("ddd.png","wb") as destFile:
  for line in srcFile:
    destFile.write(line)

6.文件对象常用方法和属性

文件对象的属性
在这里插入图片描述
文件对象的打开模式
在这里插入图片描述
文件对象的常用方法
在这里插入图片描述
文件任意位置操作

#e.txt的内容是:abcefghljklmn
with open("e.txt","r",encoding="utf-8") as f:
  print("文件名是:{0}".format(f.name)) #文件名是:e.txt
  print(f.tell())           #0
  print("读取的内容:{0}".format(str(f.readline()))) #读取的内容:abcdefghijklmn
  print(f.tell())           #14
  f.seek(3,0)
  print("读取的内容:{0}".format(str(f.readline()))) #读取的内容:defghijklmn

7.使用pickle实现序列化和反序列化

序列化指的是:将对象转化成“串行化”数据形式,存储到硬盘或通过网络传输到其他地方。反序列化是指相反的过程,将读取到的“串行化数据”转化成对象。
pickle.dump(obj, file) obj就是要被序列化的对象,file指的是存储的文件
pickle.load(file) 从file读取数据,反序列化成对象

【操作】将对象序列化到文件中

import pickle
with open("data.dat","wb") as f:  #写入文件序列化
  name = "高淇"
  age = 34
  score = [90,80,70]
  resume = {'name':name,'age':age,'score':score}
​
  pickle.dump(resume,f)

【操作】将获得的数据反序列化成对象

import pickle
with open("data.dat","rb") as f:
  resume = pickle.load(f)
  print(resume)

8.CSV文件的读取和写入

csv是逗号分隔符文本格式,常用于数据交换、Excel文件和数据库数据的导入和导出。
与Excel文件不同,CSV文件中:

  • 值没有类型,所有值都是字符串
  • 不能指定字体颜色等样式
  • 不能指定单元格的宽高,不能合并单元格
  • 没有多个工作表
  • 不能嵌入图像图表

【操作】csv.reader对象于从csv文件读取数据

import csv
with open(r"d:\a.csv") as a:
  a_csv = csv.reader(a)    #创建csv对象,它是一个包含所有数据的列表,每一行为一个元素
  headers = next(a_csv)    #获得列表对象,包含标题行的信息
  print(headers)
  for row in a_csv:      #循环打印各行内容
    print(row)

【操作】csv.writer对象写一个csv文件

import csv
​
headers = ["工号","姓名","年龄","地址","月薪"]
rows = [("1001","高淇",18,"西三旗1号院","50000"),("1002","高八",19,"西三旗1号院","30000")]
​
with open(r"d:\b.csv","w") as b:
  b_csv = csv.writer(b)    #创建csv对象
  b_csv.writerow(headers)   #写入一行(标题)
  b_csv.writerows(rows)    #写入多行(数据)

9.os模块

os模块可以直接对操作系统进行操作。我们可以直接调用操作系统的可执行文件、命令,直接操作文件、目录等等。

9.1 调用可执行程序

【示例】os.system调用程序

import os
os.system("notepad.exe")
os.system("ping www.baidu.com")
os.startfile(r"C:\Program Files (x86)\Tencent\WeChat\WeChat.exe")

9.2 获取文件信息

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

#测试os模块中,关于文件和目录的操作
import os
​
#打印基本的信息
print(os.name) #windows-->nt  linux-->posix
print(os.sep)  #windows-->\  linux-->/
print(repr(os.linesep))  #windows-->\r\n  linux-->\n
a = '3'
print(a)
print(repr(a))   #repr可以显示数据信息
#获取文件和文件夹的相关信息
print(os.stat("my01.py"))
#关于工作目录的操作
print(os.getcwd())  #获得当前工作目录
os.chdir("d:")   #当前的工作目录就变成了d:的根目录
######创建目录、删除目录
#os.mkdir("书籍")
#os.rmdir("书籍")
######创建多级目录
# os.makedirs("电影/港台/周星驰")
# os.rename("电影","movie")
dirs = os.listdir("movie")
print(dirs)

9.3 os.path模块

os.path模块提供了目录相关(路径判断、路径切分、路径连接、文件夹遍历)的操作
在这里插入图片描述
【示例】测试os.path中常用方法

#测试os.path常用方法
import os
import os.path
​
#################获得目录、文件基本信息######################
print(os.path.isabs("d:/a.txt"))  #是否绝对路径
print(os.path.isdir("d:/a.txt"))  #是否目录
print(os.path.isfile("d:/a.txt"))  #是否文件
print(os.path.exists("a.txt"))   #文件是否存在
print(os.path.getsize("a.txt"))   #文件大小
print(os.path.abspath("a.txt"))   #输出绝对路径
print(os.path.dirname("d:/a.txt")) #输出所在目录########获得创建时间、访问时间、最后修改时间##########
print(os.path.getctime("a.txt"))  #返回创建时间
print(os.path.getatime("a.txt"))  #返回最后访问时间
print(os.path.getmtime("a.txt"))  #返回最后修改时间################对路径进行分割、连接操作####################
path = os.path.abspath("a.txt")   #返回绝对路径
print(os.path.split(path))     #返回元组:目录、文件 ('C:\\Users\\Administrator\\PycharmProjects\\mypro_io\\test_os', 'a.txt')
print(os.path.splitext(path))    #返回元组:路径、扩展名 ('C:\\Users\\Administrator\\PycharmProjects\\mypro_io\\test_os\\a', '.txt')
print(os.path.join("aa","bb","cc")) #返回路径:aa/bb/cc

【示例】列出指定目录下所有的.py文件,并输出文件名

#列出指定目录下所有的.py文件,并输出文件名
import os
import os.path
​
path = os.getcwd()
file_list = os.listdir(path) #列出子目录和子文件
for filename in file_list:
    pos = filename.rfind(".")
    if filename[pos+1:]=="py":
      print(filename,end="\t")
​
print("#######使用推导式###########")
file_list2 = [filename for filename in os.listdir(path) if filename.endswith(".py") ]
for filename in file_list2:
  print(filename,end="\t")

9.4 使用walk递归遍历所有子目录和子文件

os.walk() 方法是文件、目录遍历器,高效的处理文件、目录方面的事情。格式如下:
os.walk(top[, topdown=True[, οnerrοr=None[, followlinks=False]]])
其中,top:是要遍历的目录。topdown:可选,True,先遍历top目录再遍历子目录。
返回三元组(root、dirs、files):
root:当前正在遍历的文件夹本身
dirs:一个列表,该文件夹中所有的目录的名字
files:一个列表,该文件夹中所有的文件的名字

import os
​
path = os.getcwd()
list_files = os.walk(path,topdown=False)for root,dirs,files in list_files:
  for name in files:
    print(os.path.join(root,name))
  for name in dirs:
    print(os.path.join(root,name))

10.shutil模块

shutil模块是python标准库中提供的,主要用来做文件和文件夹的拷贝、移动、删除等;还可以做文件和文件夹的压缩、解压缩操作。
os模块提供了对目录或文件的一般操作。shutil模块作为补充,提供了移动、复制、压缩、解压等操作,这些os模块都没有提供。

import shutil
#copy文件内容
shutil.copyfile("a.txt","a_copy.txt")
#递归拷贝,将电影文件夹的学习文件夹拷贝到音乐里,可以忽略固定格式的文件
#"音乐"文件夹不存在才能用。
shutil.copytree("电影/学习","音乐",ignore=shutil.ignore_patterns("*.html","*.htm"))

【示例】实现将文件夹所有内容压缩(使用shutil模块)

import shutil
import zipfile
#将"电影/学习"文件夹下所有内容压缩到"音乐2"文件夹下生成movie.zip
#shutil.make_archive("音乐2/movie","zip","电影/学习")#压缩:将指定的多个文件压缩到一个zip文件
#z = zipfile.ZipFile("a.zip","w")
#z.write("1.txt")
#z.write("2.txt")
#z.close()

【示例】实现将压缩包解压缩到指定文件夹(使用shutil模块)

import shutil
import zipfile
#解压缩:
z2 = zipfile.ZipFile("a.zip","r")
z2.extractall("d:/") #设置解压的地址
z2.close()

11.递归遍历目录下所有文件

import os
allfile=[]

def getFiles(path,level):
  childFiles = os.listdir(path)
  for file in childFiles:
    filepath = os.path.join(path,file)
    if os.path.isdir(filepath):
      getFiles(filepath,level+1)
    allfile.append("\t"*level+filepath)


getFiles(os.getcwd(),0)

for f in reversed(allfile):
  print(f)

12.目录树结构的展示

import os
import os.path
​
#递归遍历目录树
def my_print_file(path,level):
  child_files = os.listdir(path)
  for file in child_files:
    file_path = os.path.join(path,file)
    print("\t"*level+file_path[file_path.rfind(os.sep)+1:])
    if os.path.isdir(file_path):
      my_print_file(file_path,level+1)
my_print_file("电影",0)

五、模块

1.模块化

在这里插入图片描述
Python程序由模块组成。一个模块对应python源文件,一般后缀名是:.py
模块由语句组成。运行Python程序时,按照模块中语句的顺序依次执行
语句是Python程序的构造单元,用于创建对象、变量赋值、调用函数、控制语句等。

2.模块化编程流程

模块化编程的一般流程:

  • 设计API,进行功能描述。
  • 编码实现API中描述的功能。
  • 在模块中编写测试代码,并消除全局代码。
  • 使用私有函数实现不被外部客户端调用的模块函数

模块的API和功能描述要点

API(Application Programming Interface 应用程序编程接口)是用于描述模块中提供的函数和类的功能描述和使用方式描述。
模块化编程中,首先设计的就是模块的API(即要实现的功能描述),然后开始编码实现API中描述的功能。最后,在其他模块中导入本模块进行调用。
可以通过help(模块名)查看模块的API。一般使用时先导入模块 然后通过help函数查看。

【示例】设计计算薪水模块的API

#encoding=utf-8
"""
本模块用于计算公司员工的薪资
"""
company = "北京尚学堂"
​
def yearSalary(monthSalary):
  """根据传入的月薪,计算出年薪"""
  pass
​
def daySalary(monthSalary):
  """根据传入的月薪,计算出每天的薪资"""
  pass

如上模块只有功能描述和规范,需要编码人员按照要求实现编码。
我们可以通过__doc__可以获得模块的文档字符串的内容。源代码如下:

#encoding=utf-8
import salary
print(salary.__doc__)
print(salary.yearSalary.__doc__)

模块的创建和测试代码
每个模块都有一个名称,通过特殊变量__name__可以获取模块的名称。在正常情况下,模块名字对应源文件名。 仅有一个例外,就是当一个模块被作为程序入口时(主程序、交互式提示符下),它的__name__的值为__main__。我们可以根据这个特点,将模块源代码文件中的测试代码进行独立的处理。例如:
import math
print(math.name) #输出’math’

【示例】通过__name==“main”独立处理模块的测试代码

#encoding=utf-8
"""
本模块用于计算公司员工的薪资
"""
company = "北京尚学堂"
​
def yearSalary(monthSalary):
  """根据传入的月薪,计算出年薪"""
  return monthSalary*12
​
def daySalary(monthSalary):
  """根据传入的月薪,计算出每天的薪资"""
  return monthSalary/22.5   #国家规定每个月的平均工作日是22.5if __name__ =="__main__":    #测试代码
  print(yearSalary(3000))
  print(daySalary(3000))

模块文档字符串和API设计
在模块的第一行增加一个文档字符串,用于描述模块的相关功能。然后,通过__doc__可以获得文档字符串的内容。

#encoding=utf-8
import MySalary

print(MySalary.__doc__)
print(MySalary.yearSalary.__doc__)

在正常情况下,模块名字对应源文件名。 仅有一个例外,就是当一个模块被作为程序入口时(主程序、交互式提示符下),它的__name__的值为__main__。我们可以根据这个特点,将模块源代码文件中的测试代码进行独立的处理。

3.模块导入

import语句导入

import 模块名 #导入一个模块
import 模块1,模块2… #导入多个模块
import 模块名 as 模块别名 #导入模块并使用新名字

import加载的模块分为四种类型:

  • 使用python编写的代码.py文件
  • 已被编译为共享库或DLL的C或C++扩展
  • 一组模块的包
  • 使用C编写并链接到python解释器的内置模块

一般通过import语句实现模块的导入和使用,import本质上是使用了内置函数__import__()。
当通过import导入一个模块时,python解释器进行执行,最终会生成一个对象,这个对象就代表了被加载的模块。

import math
​
print(id(math))
print(type(math))
print(math.pi)  #通过math.成员名来访问模块中的成员

31840800
<class ‘module’>

math模块被加载后,实际会生成一个module类的对象,该对象被math变量引用。我们可以通过math变量引用模块中所有的内容。通过import导入多个模块,本质上也是生成多个module类的对象而已。

from…import导入

from 模块名 import 成员1,成员2,…

导入一个模块中的所有成员: from 模块名 import *
尽量避免from 模块名 import *这种写法。*它表示导入模块中所有的不是以下划线_开头的名字都导入到当前位置。 但你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差。

from math import pi,sin
print(sin(pi/2))  #输出1.0

import语句和from…import语句的区别
import导入的是模块。from…import导入的是模块中的函数/类。进行类比的话,import导入的是“文件”,我们要使用该“文件”下的内容,必须前面加“文件名称”。from…import导入的是文件下的“内容”,直接使用这些“内容”即可,前面再也不需要加“文件名称”了。

自定义一个模块calculator.py:

"""一个实现四则运算的计算器"""
​
def add(a,b):
  return a+b
def minus(a,b):
  return a-b
class MyNum():
  def print123(self):
    print(123)

在另一个模块test.py测试:

import calculator
a = calculator.add(30,40)
# add(100,200)    #不加模块名无法识别
print(a)
from calculator import *
a = add(100,200)  #无需模块名,可以直接引用里面的函数/类
print(a)
b = MyNum()
b.print123()

4.import加载底层原理

import()动态导入
import语句本质上就是调用内置函数__import__(),我们可以通过它实现动态导入。给__import__()动态传递不同的的参数值,就能导入不同的模块
使用__import__()动态导入指定的模块:

s = "math"
m = __import__(s)  #导入后生成的模块对象的引用给变量m
print(m.pi)

一般不建议自行使用__import__()导入,其行为在python2和python3中有差异,会导致意外错误。如果需要动态导入可以使用importlib模块

import importlib
a = importlib.import_module("math")
print(a.pi)

当导入一个模块时, 模块中的代码都会被执行。不过,如果再次导入这个模块,则不会再次执行。确实需要重新加载一个模块,这时候可以使用:importlib.reload()方法:

import test02
import test02
print("####")
import importlib
importlib.reload(test02)

5.包的概念和创建包导入包

当一个项目中有很多个模块时,需要再进行组织。我们将功能类似的模块放到一起,形成了“包”。本质上,“包”就是一个必须有__init__.py的文件夹。典型结构如下:
在这里插入图片描述包下面可以包含“模块(module)”,也可以再包含“子包(subpackage)”。
在这里插入图片描述
导入包操作和本质
import a.aa.module_AA在使用时,必须加完整名称来引用,比如:a.aa.module_AA.fun_AA()
from a.aa import module_AA在使用时,直接可以使用模块名。 比如:module_AA.fun_AA()
from a.aa.module_AA import fun_AA 直接导入函数在使用时,直接可以使用函数名。 比如:fun_AA()

from package import item 这种语法中,item可以是包、模块,也可以是函数、类、变量。
import item1.item2 这种语法中,item必须是包或模块,不能是其他。
导入包的本质其实是“导入了包的__init__.py”文件。也就是说,import pack1意味着执行了包pack1下面的__init__.py文件。 这样,可以在__init__.py中批量导入需要的模块,而不再需要一个个导入。
init.py核心作用:

  • 作为包的标识,不能删除
  • 导入包实质是执行__init__.py文件,可以在__init__.py文件中做这个包的初始化、以及需要统一执行代码、批量导入。

【示例】测试包的__init__.py文件本质用法
a包下的__init__.py文件内容:

import turtle
import math
print("导入a包")

b包下的module_B1.py文件中导入a包

import a
print(a.math.pi)

6.包的模糊导入

import * 这样的语句理论上是希望文件系统找出包中所有的子模块,然后导入它们。这可能会花长时间等。Python 解决方案是提供一个明确的包索引。

这个索引由 init.py 定义 all 变量,该变量为一个列表,如上例 a包下的 init.py 中,可定义 all = [“module_A”,“module_A2”]

这意味着, from sound.effects import * 会从对应的包中导入以上两个子模块.

7.PIP安装第三方库

命令行下远程安装
pip更换数据源(由于访问国外网站慢,建议更换):
家目录中,创建pip目录,然后增加文件:pip.ini 内容拷贝下面的即可(不要加其他字符):

[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
Linux的家目录: ~ 增加目录和文件:~/.pip/pip.conf
Windows的家目录是: c:/user/用户名 增加目录和文件:c:/user/用户名/pip/pip.ini

其他数据源:

阿里云 http://mirrors.aliyun.com/pypi/simple/
豆瓣:http://pypi.douban.com/simple/
中国科学技术大学 : https://pypi.mirrors.ustc.edu.cn/simple
清华:https://pypi.tuna.tsinghua.edu.cn/simple
华中理工大学 : http://pypi.hustunique.com/simple
山东理工大学 : http://pypi.sdutlinux.org/simple
V2EX:http://pypi.v2ex.com/simple

Pycharm中直接安装到项目中
在Pycharm中,依次点击:file–>setting–>Project 本项目名–>Project Interpreter,点击+,然后输入要安装的第三方库。
在这里插入图片描述

总结

111

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值