类和对象
类和对象是面向对象编程的两个核心概念。
类
类是对一群具有相同特征或者行为 的事物的一个统称,是抽象的,不能直接使用
特征其实就是一个变量,在类里我们称之为属性。
行为其实就是一个函数,在类里我们称之为方法。
类其实就是由 属性 和 方法 组成的一个抽象概念。
对象
对象是由类创建出来的一个具体存在,可以直接使用。由哪一个类创建出来的
对象,就拥有在哪一个类中定义的属性和方法
应该先有类,在类里定义好属性和行为,再根据类来创建对象
类和对象的关系
类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象。
使用同一个类,能够创建出很多对象。
类中定义了什么属性和方法,对象中就有什么属性和方法。
不同对象对应的属性值也会不同。
类的设计
类名 这类事物的名字,安照大驼峰命名法(每个单词的首字母大写)起名。
属性 这类事物具有什么样的特征。
方法 这类事物具有什么样的行为。
属性和方法的确定
对对象的特征描述,可以定义成属性
对象具有的行为(动词)可以定义成方法
定义类
在Python中要定义一个只包含方法的类,语法格式如下:
class 类名:
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass
方法的定义格式和之前学习过的函数一样
方法里的第一个参数必须是self
类名要遵守大驼峰命名法。
创建实例对象
当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量名 = 类名()
class student(object):
def study(self):
print("学习")
def eat(self):
print("干饭")
stu=student()
stu.study()
stu.eat()
给对象添加属性
python支持动态属性,当一个对象创建好了以后,直接使用 对象.属性名 = 属性值 就可以很方便的给对象添加一个属性。
class student(object):
def study(self):
print("学习")
def eat(self):
print("干饭")
stu=student()
stu.study()
stu.eat()
stu.name='dyk'
print(stu.name) # 可以直接给 stu 对象添加一个 name 属性
#这种方法很方便,但是,不建议使用这种方式给对象添加属性
#可以用后面的 __slots__ = ()来阻止
self的概念
哪个对象调用了方法,方法里的self指的就是谁。 通过 self.属性名 可以访问到这个对象的属性;通过 self.方法名() 可以调用这个对象的方法。
有点类似于java里面的this
魔法方法
Python 里有一种方法,叫做魔法方法。Python 的类里提供的,两个下划线开始,两个下划线结束的方法,就是魔法方法,魔法方法在恰当的时候就会被激活,自动执行。
魔法方法的两个特点:
两侧各有两个下划线
名字已经由 Python 官方定义好,不能改变
__init__方法
__init__()方法,在创建一个对象时默认被调用,不需要手动调用。在开发中,
如果希望在创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行改造。
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def study(self):
print("学习")
def eat(self):
print("干饭")
#stu=student() #报错 必须指定参数
stu=student('dyk',16,1)
print(stu.name) #dyk
print(stu.age) #16
注意:
__init__()方法在创建对象时,会默认被调用,不需要手动的调用这个方法。
__init__()方法里的self参数,在创建对象时不需要传递参数,python解释器会把创建好的对象引用直接赋值给self
会默认调用objec的__new__方法申请空间
在类的内部,可以使用self来使用属性和调用方法;在类的外部,需要使用对象名来使用属性和调用方法。
如果有多个对象,每个对象的属性是各自保存的,都有各自独立的地址。
方法是所有对象共享的,只占用一份内存空间,方法被调用时会通过self来判断是哪个对象调用了实例方法。
__del__方法
创建对象后,python解释器默认调用__init__()方法;
而当删除对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法。
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def __del__(self):
print("del方法被调用")
stu=student('dyk',16,1) #del方法被调用
因为程序结束会删除对象所以会调用del方法
__str__方法
__str__方法返回对象的描述信息,使用print()函数打印对象时,其实调用的就是这个对象的__str__方法。
有点类似于java里面的tostring方法
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
stu=student('dyk',16,1)
# 使用 print 方法打印对象时,会调用对象的 __str__ 方法,默认会打印类名和对象的地址名
print(stu) #<__main__.student object at 0x0000028597DA7640>
如果想要修改对象的输出的结果,可以重写 str 方法
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def __str__(self):
return "姓名:{} 年龄: {} 学号:{}".format(self.name,self.age,self.id)
stu=student('dyk',16,1)
print(stu) #姓名:dyk 年龄: 16 学号:1
__repr__方法
__repr__方法和__str__方法功能类似,都是用来修改一个对象的默认打印内
容。在打印一个对象时,如果没有重写__str__方法,它会自动来查找__repr__方法。
如果这两个方法都没有,会直接打印这个对象的内存地址。
如果两个方法都有还是会调用__str__方法
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def __str__(self):
return "姓名:{} 年龄: {} 学号:{}".format(self.name,self.age,self.id)
def __repr__(self):
return "name:{} age: {} id:{}".format(self.name,self.age,self.id)
stu=student('dyk',16,1)
print(stu) #姓名:dyk 年龄: 16 学号:1
__call__方法
对象后面加括号,触发执行。可以将对象当做方法一样执行
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def __call__(self, *args, **kwargs):
return "dyk真帅啊"
stu=student('dyk',16,1)
str=stu()
print(str)
总结
当创建一个对象时,会自动调用__init__方法,当删除一个对象时,会自动调用
__del__方法。
使用__str__和__repr__方法,都会修改一个对象转换成为字符串的结果。一般
来说,__str__方法的结果更加在意可读性,而__repr__方法的结果更加在意正
确性(例如:datetime模块里的datetime类)
运算相关的魔法方法
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
stu=student('dyk',16,1)
stu1=student('dyk',16,1)
print(stu==stu1) #False
上述代码中,使用==运算符比较两个对象,结果是false,因为如果没重写__eq__方法 ==默认的是比较地址
重写完__eq__方法后
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def __eq__(self, other):
return self.id==other.id and self.name==other.name and self.age==other.age
stu=student('dyk',16,1)
stu1=student('dyk',16,1)
print(stu==stu1) #True
如果定义了__ne__则在不等于的时候先判断,如果没定义,则会根据__eq__的非来判断
比较运算符相关魔法方法
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def __eq__(self, other):
return self.id==other.id and self.name==other.name and self.age==other.age
#def __ne__(self, other):
def __gt__(self, other):
return self.age>other.age
def __lt__(self, other):
return self.age < other.age
stu=student('dyk',16,1)
stu1=student('cb',18,2)
print(stu > stu1) #False
print(stu<stu1) #True
算数运算符相关魔法方法
def __add__(self, other):
def __sub__(self, other):
def __mul__(self, other):
def __truediv__(self, other):
def __mod__(self, other):
def __pow__(self, other):
类型转换相关魔法方法
def __int__(self):
def __float__(self):
def __str__(self):
def __bool__(self):
内置属性
使用内置函数dir可以查看一个对象支持的所有属性和方法,Python中存在着很多的内置属性。
___slots __
Python中支持动态属性,可以直接通过点语法直接给一个对象
添加属性,代码更加的灵活。但是在某些情况下,我们可能需
要对属性进行控制,此时,就剋使用__slots__实现。
class student(object):
__slots__ = ('name','age','id')
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
stu=student('dyk',18,1)
print(stu.name)
print(stu.age)
# 对象p只能设置name和age,id属性,不能再动态添加属性
stu.height=180
print(stu) #报错
用了__slots__ 就不能再动态添加属性了
__ doc__
表示类的描述信息。
class Foo:
""" 描述类信息,这是用于看片的神奇 """
def func(self):
pass
print(Foo.__doc__)
#输出:类的描述信息
__ module__ 和 __ class__
module 表示当前操作的对象在那个模块;class 表示当前操作的对象的类是什么
class student(object):
__slots__ = ('name','age','id')
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
stu=student('dyk',18,1)
print(stu.__module__) #__main__
print(stu.__class__) #<class '__main__.student'>
__ dict__
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
# 获取类的属性,即:类属性、方法
print(student.__dict__)
stu=student('dyk',18,1)
#获取 对象stu 的属性
print(stu.__dict__) #{'name': 'dyk', 'age': 18, 'id': 1}
_getitem、_setitem__和__delitem__方法
这三个方法,是将对象当做字典一样进行操作。
class student(object):
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
def __setitem__(self, key, value):
print("__setitem__",key,value)
def __getitem__(self, item):
print('__getitem__', item)
def __delitem__(self, key):
print('__delitem',key)
stu=student('dyk',18,1)
stu['name']='cb' # 自动触发执行 __setitem__
rs=stu['name'] # 自动触发执行 __getitem__
del stu['name'] # 自动触发执行 __delitem__
实例属性
通过类创建的对象被称为 实例对象,对象属性又称为实例属性,记录对象各自的数据,不同对象的同名实例属性,记录的数据各自独立,互不干扰
class student(object):
def __init__(self,name,age,id):
# 这里的name和age,id都属于是实例属性,每个实例在创建时,都有自己的属性
self.name=name
self.age=age
self.id=id
# 每创建一个对象,这个对象就有自己的name和age,id属性
stu=student('dyk',18,1)
stu1=student('cb',20,2)
类属性
类属性就是类对象所拥有的属性,它被该类的所有实例对象所共有,类属性可以通过类对象或者实例对象访问。
class student(object):
schoolname='wsyu' #类属性
def __init__(self,name,age,id):
# 这里的name和age,id都属于是实例属性,每个实例在创建时,都有自己的属性
self.name=name
self.age=age
self.id=id
# 每创建一个对象,这个对象就有自己的name和age,id属性
stu=student('dyk',18,1)
stu1=student('cb',20,2)
print(stu.schoolname) #wsyu
print(stu1.schoolname) #wsyu
print(student.schoolname) #wsyu
使用场景:
类的实例记录的某项数据始终保持一致时,则定义类属性。
/实例属性要求每个对象为其单独开辟一份内存空间来记录数据,而类属性为全类所共有 ,仅占用一份内存,更加节省内存空间。
注意点:
尽量避免类属性和实例属性同名。如果有同名实例属性,实例对象会优先访问实例属性。
class student(object):
schoolname='wsyu' #类属性
name='dyk66'
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
stu=student('dyk',18,1)
stu1=student('cb',20,2)
#类属性和实例属性同名。如果有同名实例属性,实例对象会优先访问实例属性
print(stu.name) #dyk
类属性只能通过类对象修改,不能通过实例对象修改
class student(object):
schoolname='wsyu' #类属性
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
stu=student('dyk',18,1)
stu1=student('cb',20,2)
print(stu.schoolname) #wsyu
stu.schoolname='sy' # 相当于增加了一个实例属性,并不会改变类属性
print(stu.schoolname) #sy
print(student.schoolname) #wsyu
# 只有使用类名才能修改类属性
student.schoolname='sy'
print(student.schoolname) #sy
类属性也可以设置为私有,前边添加两个下划线
class student(object):
schoolname='wsyu' #类属性
__type='sili'
def __init__(self,name,age,id):
self.name=name
self.age=age
self.id=id
print(student.schoolname) #wsyu
#print(student.__type)# 错误,私有属性,外部无法访问。
私有属性和方法
在实际开发中,对象的某些属性或者方法可能只希望在对象的内部别使用,而不希望在外部被访问到,这时就可以定义私有属性和私有方法
定义方法
在定义属性或方法时,在属性名或者方法名前增加两个下划线__,定义的就是私有属性或方法
class student(object):
schoolname='wsyu' #类属性
__type='sili'
def __init__(self,name,age):
self.name=name
self.age=age
self.__id=1001 # 使用 __ 修饰的属性,是私有属性
def __change(self):
self.__id=1111
stu=student('dyk',18)
#print(stu.id) #这里会报错,不能直接访问对象内部的私有属性
#stu.__change() 这里会报错,__change 只能在对象内部使用,外部无法访问
访问私有属性和方法
私有属性不能直接使用,私有方法不能直接调用。但是,通过一些代码,我们也可以在外部访问一个对象的私有属性和方法。
直接访问
使用方式:在私有属性名或方法名前添加 _类名
class student(object):
schoolname='wsyu' #类属性
__type='sili'
def __init__(self,name,age):
self.name=name
self.age=age
self.__id=1001 # 使用 __ 修饰的属性,是私有属性
def __change(self):
self.__id=1111
stu=student('dyk',18)
#print(stu.id) #这里会报错,不能直接访问对象内部的私有属性
#stu.__change() 这里会报错,__change 只能在对象内部使用,外部无法访问
# 使用对象名._类名__私有属性名 可以直接访问对象的私有属性
print(stu._student__id) #1001
# 使用对象名._类名__函数名 可以直接调用对象的私有方法
stu._student__change()
print(stu._student__id) #1111
注意:在开发中,我们强烈不建议使用 对象名._类名__私有属性名 的方式来访问对象的私有属性!
定义方法访问私有变量
class student(object):
schoolname='wsyu' #类属性
__type='sili'
def __init__(self,name,age):
self.name=name
self.age=age
self.__id=1001 # 使用 __ 修饰的属性,是私有属性
def __change(self):
self.__id=1111
def set_id(self,id): # 定义了set_id方法,在这个方法里,可以修改 __id
self.__id=id
def get_id(self): # 定义了get_id 方法,在这个方法里获取到 __id
return self.__id # 内部可以访问 __id 变量
stu=student('dyk',18)
print(stu.get_id()) #1001
stu.set_id(1234)
print(stu.get_id()) #1234
类方法、静态方法
类方法
第一个形参是类对象的方法
需要用装饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数。
class student(object):
schoolname='wsyu' #类属性
def __init__(self,name,age):
self.name=name
self.age=age
# 类方法,用classmethod来进行修饰
@classmethod
def gettype(cls):
return cls.schoolname
print(student.gettype()) #wsyu
使用场景:
当方法中 需要使用类对象 (如访问私有类属性等)时,定义类方法
类方法一般和类属性配合使用
静态方法
需要通过装饰器@staticmethod来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。
静态方法 也能够通过 实例对象 和 类对象 去访问。
class student(object):
schoolname='wsyu' #类属性
def __init__(self,name,age):
self.name=name
self.age=age
@staticmethod
def study():
print('学习') # 静态方法不会自动传递实例对象和类对象
stu=student('dyk',18)
student.study() # 可以用 类对象 来调用 静态方法
stu.study() # 可以用 实例对象 来调用 静态方法
使用场景:
当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
__new__和__init__方法
__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python
解释器自动提供
__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要
特别注意,可以return父类__new__出来的实例,或者直接是object的__new__
出来的实例
__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__
的基础上可以完成一些其它初始化的动作,__init__不需要返回值
单例设计模式
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。
# 实例化一个单例
class Singleton(object):
__instance = None
__is_first = True
def __new__(cls, age, name):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
def __init__(self, age, name):
if self. __is_first: # 不会再创建第二个对象
self.age = age
self.name = name
Singleton. __is_first = False
a = Singleton(18, "张三")
b = Singleton(28, "张三")
print(id(a))
print(id(b))
print(a.age) # 18
print(b.age) # 18
a.age = 19
print(b.age)
继承
继承是面向对象软件设计中的一个概念,与多态、封装共为面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。
在程序中,继承描述的是多个类之间的所属关系。
如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到类B里。
那么类A就是基类,也叫做父类;类B就是派生类,也叫做子类
class Animal:
def __int__(self):
pass
"""动物类"""
def sleep(self):
print('正在睡觉')
class Dog(Animal):
"""Dog类继承自Animal类"""
def __init__(self):
pass
class Cat(Animal): # 定义类时,在括号后面传入父类的类名,表示子类继承父类
"""Cat类继承自Animal类"""
def __int__(self):
pass
# Dog 和 Cat 都继承自Animal类,可以直接使用Animal类里的sleep方法
dog = Dog()
dog.sleep()
cat = Cat()
cat.sleep()
在Python中,继承可以分为单继承、多继承和多层继承,这里和java不一样,java只支持单继承和多层继承
单继承
子类只继承一个父类
继承概念:子类用于父类的所有的方法和属性。
class 类名(父类名):
pass
子类继承自父类,可以享受父类中已经封装好的方法,不需要再次定义
子类中应该根据职责,封装子类特有的属性和方法。
class anmial(object):
def eat(self):
print('吃饭')
class dog(anmial): # 定义类时,在括号后面传入父类的类名,表示子类继承
def lookdoor(self):
print('看门')
d=dog()
d.eat() #吃饭
d.lookdoor() #看门
多继承
子类可以拥有多个父类,并且具有所有父类的属性和方法
class 子类名(父类名1,父类名2...)
pass
class anmial(object):
def eat(self):
print('吃饭')
class bigdog(object):
def eat(self):
print('吃骨头')
def bark(self):
print('狗叫')
class dog(anmial,bigdog):
def lookdoor(self):
print('看门')
d=dog()
d.eat() #吃饭
当交换两个父类的位置时
class anmial(object):
def eat(self):
print('吃饭')
class bigdog(object):
def eat(self):
print('吃骨头')
def bark(self):
print('狗叫')
class dog(bigdog,anmial):
def lookdoor(self):
print('看门')
d=dog()
d.eat() #吃骨头
当继承多个类的时候,谁写在前面,就先调用谁的方法
继承的传递性
子类拥有父类以及父类的父类中封装的所有属性和方法。
__ mro__
主要用于在多继承时判断方法属性的调用顺序。
Python中针对类提供了一个内置属性__mro__可以用来查看方法的搜索顺序。
class anmial(object):
def eat(self):
print('吃饭')
class bigdog(object):
def bark(self):
print('狗叫')
class dog(anmial,bigdog):
def lookdoor(self):
print('看门')
d=dog()
print(dog.__mro__) #(<class '__main__.dog'>, <class '__main__.anmial'>, <class '__main__.bigdog'>, <class 'object'>)
在调用方法时,按照__mro__的输出结果从左至右的顺序查找。
如果再当前类中找到方法,就直接执行,不再向下搜索。
如果没有找到,就顺序查找下一个类中是否有对应的方法,如果找到,就直接执行,不再继续向下搜索。
如果找到了最后一个类,依然没有找到方法,程序就会报错
对象相关的内置函数
Python中的身份运算符用来判断两个对象是否相等;
isinstance用来判断对象和类之间的关系;
issublcass用啊里判断类与类之间的关系。
身份运算符
身份运算符用来比较两个对象的内存地址,看这两个对象是否是同一个对象。
is
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person('张三', 18)
p2 = Person('张三', 18)
p3 = p1
print(p1 is p2) # False
print(p1 is p3) # True
isinstance
instance内置函数,用来判断一个实例对象是否是由某一个类(或者它的子类)实例化创建出来的
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, name, age, score):
super(Student, self).__init__(name, age)
self.score = score
class Dog(object):
def __init__(self, name, color):
self.name = name
self.color = color
p = Person('tony', 18)
s = Student('jack', 20, 90)
d = Dog('旺财', '白色')
print(isinstance(p, Person)) # True.对象p是由Person类创建出来的
print(isinstance(s, Person)) # True.对象s是有Person类的子类创建出来的
print(isinstance(d, Person)) # False.对象d和Person类没有关系
issubclass
issubclass 用来判断两个类之间的继承关系。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, name, age, score):
super(Student, self).__init__(name, age)
self.score = score
class Dog(object):
def __init__(self, name, color):
self.name = name
self.color = color
print(issubclass(Student, Person)) # True
print(issubclass(Dog, Person)) # False
多态
面向对象的三大特性:
封装:这是定义类的准则,根据对象的特点,将行为和属性抽象出来,封装到一
个类中。
继承:这是设计类的技巧。父类与子类,主要体现在代码的重用,不需要大量的编写重复代码。
多态:不同的子类调用相同的父类方法,产生不同的执行结果,可以增加代码的
外部灵活度。多态是以继承和重写父类方法为前提的,它是一种调用方法的技
巧,不会影响到类的内部设计。
class Dog(object):
def work(self): # 父类提供统一的方法,哪怕是空方法
pass
class ArmyDog(Dog): # 继承 Dog
def work(self): # 子类重写方法,并且处理自己的行为
print('追击敌人')
class DrugDog(Dog):
def work(self):
print('追查毒品')
class Person(object):
def work_with_dog(self, dog):
dog.work() # 使用狗可以根据对象的不同而产生不同的运行效果, 保障了代码的稳定性
# 子类对象可以当作父类来使用
dog = Dog()
ad = ArmyDog()
dd = DrugDog()
p = Person()
p.work_with_dog(dog)
p.work_with_dog(ad) # 同一个方法,只要是 Dog 的子类就可以传递,提供了代码的灵活性
p.work_with_dog(dd) # 并且传递不同对象,最终 work_with_dog 产生了不同的执行效果
定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相
同父类方法,可以产生不同的执行结果
好处:调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适
应需求的不断变化!
实现步骤:
定义父类,并提供公共方法
定义子类,并重写父类方法
传递子类对象给调用者,可以看到不同子类执行效果不同
文件IO
文件的打开与关闭
打开文件
在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件
open(文件路径,访问模式)
f = open('test.txt', 'w')
文件路径
文件的路径分为相对路径和绝对路径两种。
绝对路径:指的是绝对位置,完整地描述了目标的所在地,所有目录层级关系是
一目了然的。
例如:D:\pycharm\pycharm2020\PyCharm2020(64bit)
从电脑的盘符开始,表示的就是一个绝对路径。
相对路径:是从当前文件所在的文件夹开始的路径。
test.txt,是在当前文件夹查找 test.txt 文件
./test.txt,也是在当前文件夹里查找test.txt文件, ./ 表示的是当前文件
夹。
../test.txt,从当前文件夹的上一级文件夹里查找 test.txt 文件。 ../ 表
示的是上一级文件夹,可以多次调用
demo/test.txt,在当前文件夹里查找 demo这个文件夹,并在这个文件夹里查找
test.txt文件。
访问模式 | 说明 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,则报错。这是默认模式。 |
w | 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
a | 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
r+ | 打开一个文件用于读写。文件指针将会放在文件的开头 |
w+ | 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
a+ | 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。 |
rb | 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。 |
wb | 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
ab | 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
rb+ | 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头 |
wb+ | 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。 |
ab+ | 以二进制格式打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。 |
二进制可以用来读写图片等其他非文本数据
关闭文件
# 新建一个文件,文件名为:test.txt
f = open('test.txt', 'w')
# 关闭这个文件
f.close()
文件的读写
写数据(write)
使用write()可以完成向文件写入数据
file=open('123.txt','w',encoding='utf8')
file.write("dyk666")
file.close()
如果文件不存在,那么创建;如果存在那么就先清空,然后写入数据
读数据(read)
使用read(num)可以从文件中读取数据,num表示要从文件中读取的数据的长度(单位是字节),并且文件的指针就移动到对应字节的位置,如果没有传入num,那么就表示读取文件中所有的数据
如果不省略,则读取文件中的size个字符,并返回一个字符串
如果省略,则读取文件中的size个字符并返回一个字符串
file=open('123.txt','r',encoding='utf8')
firstread=file.read(3) # 最多读取3个数据 文件指针将移动到3这里来
print(firstread)
print('--------------')
lastread=file.read() # 从上次读取的位置继续读取剩下的所有的数据
print(lastread)
file.close() # 关闭文件
# dyk
# --------------
# 666
如果用open打开文件时,如果使用的"r",那么可以省略 open(‘test.txt’)
读数据(readline)
readline只用来读取一行数据。直到最后一行,如果还执行,则返回空字符串
同样,如果给readline(size)参数,则读取相应行的size个参数
file=open('123.txt','r',encoding='utf8')
while True:
data=file.readline()
print(data)
if not data:
break
file.close()
# dyk666
#
# dyk123
#
# dyk456
读数据(readlines)
readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行为列表的一个元素。
file=open('123.txt','r',encoding='utf8',newline=None)
data=file.readlines()
print(type(data))
for line in data:
print(line)
file.close()
# <class 'list'>
# dyk666
#
# dyk456
#
# dyk123
指针定位
tell()
tell() 方法用来显示当前指针的位置
#dyk66612345678
file=open('123.txt','r',encoding='utf8',newline=None)
file.read(3) # read 指定读取的字节数
print(file.tell()) #3 # tell()方法显示当前文件指针所在的文字
file.read()
print(file.tell())#16
seek(offset,whence) 方法用来重新设定指针的位置。
offset:表示偏移量
whence:只能传入0,1,2中的一个数字。
0表示从文件头开始计算指针的偏移量,默认值为0,这时offset必须是大于等于0
的整数
1表示从当前位置开始开始计算指针的偏移量,如果offset是负数表示从当前位置
向前移动,如果offset是正数表示当前位置向后移动
2 表示从文件的末尾开始
f = open('123.txt','rb') # 需要指定打开模式为rb,只读二进制模式
print(f.read(3))
print(f.tell())
f.seek(2,0) # 从文件的开头开始,跳过两个字节
print(f.read())
f.seek(1,1) # 从当前位置开始,跳过一个字节
print(f.read())
f.seek(-4,2) # 从文件末尾开始,往前跳过四个字节
print(f.read())
f.close()
CSV文件
CSV文件:Comma-Separated Values,中文叫逗号分隔值或者字符分割值,其文件以纯文本的形式存储表格数据。可以把它理解为一个表格,只不过这个表格是以纯文本的形式显示的,单元格与单元格之间,默认使用逗号进行分隔;每行数据之间,使用换行进行分隔。
name,age,score
dyk,18,98
cb,20,99
lfw,17,90
Python中的csv模块,提供了相应的函数,可以让我们很方便的读写csv文件。
CSV文件的写入
import csv
# 以写入方式打开一个csv文件
file=open('123.csv','w')
# 调用writer方法,传入csv文件对象,得到的结果是一个CSVWriter对象
w=csv.writer(file)
# 调用CSVWriter对象的writerow方法,一行行的写入数据
w.writerow(['name','age','score'])
# 还可以调用writerows方法,一次性写入多行数据,传入的必须是可迭代对象
w.writerows([['dyk', '18', '98'],['cb', '20', '99'], ['lfw', '17', '90']])
file.close()
CSV文件的读取
import csv
# 以读取方式打开一个csv文件
file=open('123.csv','r',newline=None)
# 调用csv模块的reader方法,得到的结果是一个可迭代对象
r=csv.reader(file)
# 对结果进行遍历,获取到结果里的每一行数据
for line in r:
print(line)
file.close()
内存中写入数据
除了将数据写入到一个文件以外,我们还可以使用代码,将数据暂时写入到内存里,可以理解为数据缓冲区。Python中提供了StringIO和BytesIO这两个类将字符串数据和二进制数据写入到内存里。
StringIO
StringIO可以将字符串写入到内存中,像操作文件一下操作字符串
from io import StringIO
# 创建一个StringIO对象
f = StringIO()
# 可以像操作文件一下,将字符串写入到内存中
f.write('hello\r\n')
f.write('good')
# 使用文件的 readline和readlines方法,无法读取到数据
# print(f.readline())
# print(f.readlines())
# 需要调用getvalue()方法才能获取到写入到内存中的数据
print(f.getvalue())
f.close()
BytesIO
如果想要以二进制的形式写入数据,可以使用BytesIO类,它的用法和StringIO相似,只不过在调用write方法写入时,需要传入二进制数据。
from io import BytesIO
f = BytesIO()
f.write('你好\r\n'.encode('utf-8'))
f.write('中国'.encode('utf-8'))
print(f.getvalue())
f.close()
文件的备份
# 提示输入文件
source_file_name=input('请输入要备份的文件')
# 以二进制读的方式打开文件,这样是图片也可以复制
source_file=open(source_file_name,'rb')
# 分割文件名和后缀名
names=source_file_name.rsplit('.',maxsplit=1)
# 组织新的文件名字
new_file_name=names[0]+'.bak.'+names[1]
# 创建新文件
new_file=open(new_file_name,'wb')
# 把旧文件中的数据,进行复制到新文件中
while True:
data=source_file.read(1024)
if not data:
break
new_file.write(data)
new_file.close()
source_file.close()
序列化和反序列化
通过文件操作,我们可以将字符串写入到一个本地文件。但是,如果是一个对象(例如列表、字典、元组等),就无法直接写入到一个文件里,需要对这个对象进行序列化,然后才能写入到文件里。
设计一套协议,按照某种规则,把内存中的数据转换为字节序列,保存到文件,这就是序列化,反之,从文件的字节序列恢复到内存中,就是反序列化。
Python中提供了JSON和pickle两个模块用来实现数据的序列化和反序列化。
JSON模块
JSON(JavaScriptObjectNotation, JS对象简谱)是一种轻量级的数据交换格式,它基于 ECMAScript 的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。JSON的本质是字符串
使用JSON实现序列化
JSON提供了dump和dumps方法,将一个对象进行序列化。
dumps
dumps方法的作用是把对象转换成为字符串,它本身不具备将数据写入到文件的功能。
import json
file=open('123.txt','w')
names=['dyk','cb','lfw']
#file.write(names) 出错,不能直接将列表写入到文件里
# 可以调用 json的dumps方法,传入一个对象参数
res=json.dumps(names)
# dumps 方法得到的结果是一个字符串
print(type(res))
# 可以将字符串写入到文件里
file.write(res)
file.close()
dump
dump方法可以在将对象转换成为字符串的同时,指定一个文件对象,把转换后的字符串写入到这个文件里。
import json
file=open('123.txt','w')
names=['dyk','cb','lfw']
# dump方法可以接收一个文件参数,在将对象转换成为字符串的同时写入到文件里
json.dump(names,file)
file.close()
注意:如果是一个空对象,调用dumps方法转换成为一个JSON对象,得到的结果是null(JS里的空对象)
使用JSON实现反序列化
使用loads和load方法,可以将一个JSON字符串反序列化成为一个Python对象。
loads
loads方法需要一个字符串参数,用来将一个字符串加载成为Python对象。
import json
file=open('123.txt','w')
# 调用loads方法,传入一个字符串,可以将这个字符串加载成为Python对象
names='["dyk", "cb", "lfw"]'
res=json.loads(names)
print(type(res)) #<class 'list'>
print(res) #['dyk', 'cb', 'lfw']
file.close()
import json
#["dyk","lfw","cb"]
file=open('123.txt','r')
# 调用loads方法,传入一个字符串,可以将这个字符串加载成为Python对象
names=file.read()
print(type(names))
res=json.loads(names)
print(type(res)) #<class 'list'>
print(res) #['dyk', 'cb', 'lfw']
file.close()
load
load方法可以传入一个文件对象,用来将一个文件对象里的数据加载成为Python对象。
import json
#["dyk","lfw","cb"]
# 以可读方式打开一个文件
file=open('123.txt','r')
# 调用load方法,将文件里的内容加载成为一个Python对象
res=json.load(file)
print(type(res)) #<class 'list'>
print(res) #['dyk', 'cb', 'lfw']
file.close()
pickle模块
和json模块类似,pickle模块也有dump和dumps方法可以对数据进行序列化,同时也有load和loads方法进行反序列化。区别在于,json模块是将对象转换成为字符串,而pickle模块是将对象转换成为二进制。
pickle模块里方法的使用和json里方法的使用大致相同,需要注意的是,pickle是将对象转换成为二进制,所以,如果想要把内容写入到文件里,这个文件必须要以二进制的形式打开。
import pickle
# 以二进制写的方式打开一个文件
file=open('123.txt','wb')
names=['dyk','cb','lfw']
# dump方法可以接收一个文件参数,在将对象转换成为字符串的同时写入到文件里
res=pickle.dumps(names)
print(type(res)) #<class 'bytes'>
file.write(res) #写进去可以看见全是乱码
file.close()
#以二进制读的方式打开一个文件
file=open('123.txt','rb')
data=file.read()
# 调用loads方法,传入一个字符串,可以将这个字符串加载成为Python对象
res=pickle.loads(data)
print(res) #['dyk', 'cb', 'lfw']
file.close()
json模块:
将对象转换成为字符串,不管是在哪种操作系统,哪种编程语言里,字符串都是可识别的。
json就是用来在不同平台间传递数据的。
并不是所有的对象都可以直接转换成为一个字符串,下标列出了Python对象与json字符串的对应关系。
Python | JSON |
---|---|
dict | object |
list,tuple | array |
str | string |
int,float | number |
True | true |
False | false |
None | null |
如果是一个自定义对象,默认无法装换成为json字符串,需要手动指定JSONEncoder.
如果是将一个json串重新转换成为对象,这个对象里的方法就无法使用了
import json
class MyEncode(json.JSONEncoder):
def default(self, o):
# return {"name":o.name,"age":o.age}
return o.__dict__
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(self.name+'正在吃东西')
p1 = Person('zhangsan', 18)
# 自定义对象想要转换成为json字符串,需要给这个自定义对象指定JSONEncoder
result = json.dumps(p1, cls=MyEncode)
print(result) # {"name": "zhangsan", "age": 18}
# 调用loads方法将对象加载成为一个对象以后,得到的结果是一个字典
p = json.loads(result)
print(type(p))
pickle模块:
pickle序列化是将对象按照一定的规则转换成为二进制保存,它不能跨平台传递数据。
pickle的序列化会将对象的所有数据都保存。
异常
异常的概念
程序在运行过程中,由于我们的编码不规范,或者其他原因一些客观原因,导致我们的程序无法继续运行,此时,程序就会出现异常。如果我们不对异常进行处理,程序可能会由于异常直接中断掉。为了保证程序的健壮性,我们在程序设计里提出了异常处理这个概念
try…except
try…except语句可以对代码运行过程中可能出现的异常进行处理。 语法结构:
try:
可能会出现异常的代码块
except 异常的类型:
出现异常以后的处理语句
try:
f = open('test.txt', 'r')
print(f.read())
except FileNotFoundError:
print('文件没有找到,请检查文件名称是否正确')
try:
print(2/0)
except Exception as e:
print(e) #division by zero
try…else
在try…except…中也是如此,即如果没有捕获到异常,那么就执行else中的事情
try:
a=2/3
except Exception as e:
print(e)
else:
print('result:%f'% a) #result:0.666667
try…finally
在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等。
try:
f = open('test.txt')
try:
while True:
content = f.readline()
if len(content) == 0:
break
print(content)
except:
#如果在读取文件的过程中,产生了异常,那么就会捕获到
#比如 按下了 ctrl+c
pass
finally:
f.close()
print('关闭文件')
except:
print("没有这个文件")
with关键字
with语句实质上是一个上下文管理器,with语句后的对象都会有__enter__()和__exit__()方法。在进入到上下文时,会自动调用__enter__()方法,程序正常执行完成,或者出现异常中断的时候,都会调用__exit__()方法。
with open('123.txt','r',encoding='utf8') as file:
data=file.read(1024)
print(data) #dyk666
一种更加简洁、优雅的方式就是用 with 关键字。open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。
自定义异常
你可以用raise语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是Error或Exception类的子类
class scoreexception(Exception):
def __init__(self,score):
self.score=score
def __str__(self):
return ('分数有误,分数应该在0到100之间')
def main():
try:
a=input('请输入你的分数')
a=int(a)
if(a<0 or a>100):
raise scoreexception(a)
except scoreexception as e:
print(e)
else:
print('分数正常')
main()
# 请输入你的分数120
# 分数有误,分数应该在0到100之间
迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
可迭代对象
我们已经知道可以对list、tuple、str等类型的数据使用for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。
int整型不是iterable,即int整型不是可以迭代的
可迭代对象通过__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对
象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来
依次获取对象中的每一个数据.
那么也就是说,一个具备了__iter__方法的对象,就是一个可迭代对象。
如何判断一个对象是否可以迭代
可以使用 isinstance() 判断一个对象是否是 Iterable 对象:
from _collections_abc import Iterable
print(isinstance([], Iterable)) #True
print(isinstance(1,Iterable))#False
调用一个对象的__iter__方法,或者调用iter()内置函数,可以获取到一个可迭代对象的迭代器。
我们要想构造一个迭代器,就要实现它的next方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可。
一个实现了iter方法和next方法的对象,就是迭代器。
class MyIterator(object):
def __init__(self,n):
self.n=n;
self.count=0
# 自定义迭代器需要重写__iter__和__next__方法
def __iter__(self):
return self
def __next__(self):
if self.count<self.n:
self.count+=1
return self.count
else:
raise StopIteration #不是抛出异常还是让迭代器循环结束,否者会一直迭代
my=MyIterator(5) # 迭代器重写了__iter__方法,它本身也是一个可迭代对象
for k in my:
print(k)
# 1
# 2
# 3
# 4
# 5
raise StopIteration 停止迭代
for…in…循环的本质
for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束
生成器
生成器(generator)。生成器是一类特殊的迭代器
要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
scq=(i for i in range(5))
print(scq)#<generator object <genexpr> at 0x000001FFC2380580>
for i in scq:
print(i)
# 0
# 1
# 2
# 3
# 4
在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字的 就称为 生成器
# 生成器写法上像一个函数
def my_gen(n):
i = 0
while i < n:
# return i # 函数里的return表示函数的执行结束
yield i # yield关键字,将函数变成生成器
i += 1
G = my_gen(10)
# print(next(iter(G)))
for i in G:
print(i)
使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是
生成器)
yield关键字有两点作用:
保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
Python3中的生成器可以使用return返回最终运行的返回值,而Python2中的生成
器不允许使用return返回一个返回值(即可以使用return从生成器中退出,但
return后不能有任何表达式)
property属性
property属性是一种用起来像是实例属性一样的特殊属性,可以对应于某个方法。
property属性的定义和调用要注意一下几点:
定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
调用时,无需括号
class student(object):
@property
def study(self):
print('学习')
stu=student()
stu.study #学习