python面向对象基础知识

python基础链接

类和对象

类和对象是面向对象编程的两个核心概念。

类是对一群具有相同特征或者行为 的事物的一个统称,是抽象的,不能直接使用

特征其实就是一个变量,在类里我们称之为属性。
行为其实就是一个函数,在类里我们称之为方法。
类其实就是由 属性 和 方法 组成的一个抽象概念。

对象

对象是由类创建出来的一个具体存在,可以直接使用。由哪一个类创建出来的 
对象,就拥有在哪一个类中定义的属性和方法

应该先有类,在类里定义好属性和行为,再根据类来创建对象

类和对象的关系

类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象。

使用同一个类,能够创建出很多对象。

类中定义了什么属性和方法,对象中就有什么属性和方法。

不同对象对应的属性值也会不同。

类的设计

类名 这类事物的名字,安照大驼峰命名法(每个单词的首字母大写)起名。

属性 这类事物具有什么样的特征。

方法 这类事物具有什么样的行为。

属性和方法的确定

对对象的特征描述,可以定义成属性

对象具有的行为(动词)可以定义成方法

定义类

在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:只能传入012中的一个数字。

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 #学习

猜你喜欢

转载自blog.csdn.net/qq_44866153/article/details/112431460