系列文章目录
第一章 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对象增加了六个属性:
dict 、module 、weakref、 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 | 解释器请求退出 |
TabError | Tab 和空格混用 |
TypeError | 对类型无效的操作 |
UnboundLocalError | 访问未初始化的本地变量 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeError | Unicode 相关的错误 |
UnicodeTranslateError | Unicode 转换时错误 |
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.5
if __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