Python总结笔记(五)面向对象编程

  1. 一切皆对象
  2. 面向对象OOP
  3. 类的创建与实例化
  4. 类的内部结构
  5. 类的继承与多态
  6. 访问控制
  7. 魔法方法Magic Method
  8. 模块module
  9. 包package

1. 一切皆对象

▪︎关于类与对象操作的BIFs——类与对象操作命令
■type() 返回对象类型 type(__builtins__) #模块类型 ■ id(),查看对象id
■ dir(),查看对象下变量及函数dir(__builtins__) #查看模块下的方法 ■ issubclass(),isinstance([1,2,3],list)判断前者是不是后者类型的, super(),类,实例,调父类 ■hasattr(),getattr(),setattr(),delattr()类属性操作
■ globals(),locals(),全局与局部的名称空间
■ import(),reload(),模块的导入与重载

# 关于类与对象的BIFs
# type() 返回对象类型
# id(),memoryview(),查看对象id和内存地址
# dir(),查看对象下变量及函数
# issubclass(),isinstance()子类,实例操作
# hasattr(),getattr(),setattr(),delattr()对象属性操作
# globals(),locals(),全局与局部的名称空间
# import(),reload(),模块的导入与重载


# type(abs)
# type('str')
# type(1)
# type([])
# type(abs)
# type(__builtins__) #模块类型

l1=[1,2,3]
id(l1)
dir(__builtins__) 


# isinstance([],list)

# class list1(list):
#     name='this is list1 class'
#     #__name如果使用前双划,属性将被隐藏
# issubclass(list1,list)

# hasattr(list1,'name')
# getattr(list1,'name')
# setattr(list1,'name','use setattr to set this attr')
# getattr(list1,'name')

def foo():
    pass


isinstance([1,2.3],list)

2. 面向对象编程-OOP

▪︎基本概念:
■ 面向过程与面向对象:
面向过程:根据业务逻辑从上到下写垒代码
函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
面向对象:对函数进行分类和封装,让开发“更快更好更强…”——将代码进行封装,封装到函数中,以后调用函数就可以节省大量代码。将代码封装到函数,将不同的函数、不同的数据一起封装到一个类里面,并将类进行实例化。

  • 面向过程编程容易理解,其往往用一长段代码来实现指定功能。
  • 函数是对面向过程的一次封装
  • 面向对象是更高一层的封装

▪︎OOP的核心思想:类与对象的关系
■ 类:从近似的对象中抽象出类–共性 方法、属性
■ 对象:然后再从类实例化出对象

# 类class
# 类是对一类事物的抽象。比如汽车,门,猫。

# Python中, 类(class)的变量是所有对象共享使用, 只有一个拷贝, 所有对象修改, 都可以被其他对象所见;
# 对象(object)的变量由类的每个对象所拥有,每个对象都包含自己的一份拷贝, 不会影响其他对象;

# 之前学习的都是Python自带的数据类型,class则是我们自己定义的类型。


# 对象object
# 某类事物中的一个具体的实例,对应类。如果我家的那辆汽车,张伟办公室的大门,隔壁丽丽家的那只小花猫。

# 类与对象的关系
# 类就像是对象的模板,可以批量生产出许多的对象。
# 好比a这个变量其实是整形数字int类的一个实例
#那int这个类还可以’复刻’出许许多多的整形对象。
#这些对象共有的特征就是:整数性。

3. 类的创建与实例化

▪︎创建类
■ class关键字
■ 指定继承
■ 定义类的成员
数据成员 :☐类变量☐实例变量
方法成员:☐类方法☐实例方法
▪︎实例化类

class Student():
#类变量--共同用的属性
    student_total=0
    student_list=[]
    pass_score=1000
    student_graduated=0

    
 #初始化,魔法函数_
    def __init__(self,name,age,gender):#只要是对象的方法,都需要self开头,默认的、指向实例对象的,无需接受。后面的是自己的
        self.name=name#将传入的参数,赋值给实例变量
        self.age=age
        self.gender=gender
        self.score=0#无需传参数,入学的学生都是零分
        Student.student_total+=1
        Student.student_list.append(name)#将david加入到
    def exam(self,score):
        if score<60:
            return "fail"
        else:
            self.score+=score
            str1="你的成绩是"+str(self.score)
            if self.score>=Student.pass_score:
                Student.student_graduated+=1
            return str1
    def check(self,):
        if self.score<Student.pass_score:
            return "你还需要",Student.pass_score-self.score,"分数"
        else:
            return "你已经毕业"
        
    @classmethod
    def get_student_graduated(cls,):
        return Student.student_graduated#针对全局
    
    @classmethod
    def get_student_total(cls,):
        return Student.student_total#针对全局
    
    
    @staticmethod
    def static1():#可以不接受参数,普通方法
        return 'this is a static method from Student,同时被实例和方法调用'
    
            
david=Student('david',22,'male')#为davidStudent类型下的对象
david.exam(61)
david.exam(1000)
david.check()
Student.get_student_graduated()#针对全局
Student.get_student_total()#针对全局

4. 类的内部结构

▪︎数据成员:[用于处理类及实例对象的相关数据]
■ 类变量:在类中且在函数体外,实例之间共享
■ 实例变量:定义在方法中,作用于当前实例的类
▪︎方法成员(在类中定义的函数叫方法) :
■ 类方法
▢定义时需要使用==@classmethod装饰器==,第一个参数为cls
■ 实例方法
▢绑定到实例的方法,第一个参数为self,
■ 静态方法[普通方法]
▢定义的时候使用 @staticmethod装饰器
▢静态方法没有参数限制,不需要实例参数self和类参数cls
▢静态法可以通过类名访问,也可以通过实例访问。

  • 实例方法:是指该类的每个实例都可以调用到的方法。只有实例能调用实例方法。定义时第一个形参为self。self是指向每个独立对象的指针.在实例方法中第一个形参被命名为self,以区别其它函数。
  • 类方法:是将类本身作为对象进行操作的方法。类对象和实例都可以调用类方法。定义时以@classmethod进行装饰,其第一个参数是类,约定写为cls。实例方法和类方法都是依赖于python的修饰器来实现。 对象方法以self参数,类方法以cls参数来传递。cls是指向类的指针,在类方法中第一个形参要命名为cls.

5. 类的继承与多态

▪︎继承
■ 创建一个类时可以从新开始,也可以从已经有的类继承下来
■ super()子调用父类的方法
▪︎多态
■ 因为类具有继承关系,子类可以向上转型被看做是父类的类型,比如
无论是战士还是快递员,都是人类。
■ 有了继承关系,子类可以继承父类的所有方法和属性,当然也可以重
载父类的成员函数及属性。
■ 例如,当子类(直升机)和父类(飞机)都存在相同的fly()方法时,子
类的fly()覆盖了父类的fly(),在运行时就总是会调用子类的fly()。
■ 这就是继承带来的多态。

#继承
#一个类可以以 class newclsname():来开始全新构造(实际上会默认继承自object);
#也可以从某个已经存在的类继承。.
#继承的类叫做subclass。

#比如要构造蜘蛛这个类,可以从昆虫这个类继承。
#构建学生,雇员,战士都可以从人这个类继承。

class person():
    def speak(self,):
        return 'people can speak language!'
    def work(self,):
        print('people should have work to do!')

class fireMan(person):
    def work(self,):#同名,重载父类的方法
        print('A great work that can save people!')
    def speak2(self,):
        print(super().speak(),'FireMan needs to speak loudly!')#super()调用父类的方法

class employee(person):
    def work(self,):
        print('To gain profits for the company is our job!')

# 因为类具有继承关系,子类可以向上转型被看做是父类的类型,比如无论是战士还是快递员,都是人类。
# 也因为有了继承关系,子类可以继承父类的所有方法和属性
#当然也可以重载父类的成员函数及属性。
#例如,当子类(直升机)和父类(飞机)都存在相同的fly()方法时,子类的fly()覆盖了父类的fly()
#在运行时就总是会调用子类的fly()。这就是继承带来的多态。

fireman1=fireMan()
# fireman1.work()#多态
# fireman1.speak2()#super方法调用父类

employee1=employee()
employee1.work()
# 多态的概念是应用于Java和C#这一类强类型语言中,
# Python是弱类型语言,Python崇尚“鸭子类型”
# 在Python世界中,一切都是对象

class A:
    def bar(self):
        print ('A.bar')
class B(A):#B继承自A
    def car(self):
        print ('B.car')

#和A,B类型不相关的类C
class C():
    def sar(self):
        print ('C.sar')
    def car(self):
        print('C.car')

def fun1(obj):#接收一个参数obj
    obj.car()#调用该参数下的car()方法。而不关心obj到底是什么类型~

b1=B()
c1=C()

fun1(b1)#一切皆对象,不care是什么class
fun1(c1) #一切皆对象,不care是什么class

OOP三大特性总结

封装:封装就是将抽象的数据(变量)和行为(函数)打包,形成一个逻辑上的整体(即类);封装可以增强安全性(数据)并简化编程(函数),用户只能通过类对外接口的访问权限来使用类的成员。

继承: 一个类可以以 class newclsname():来开始全新构造(实际上会默认继承自object);也可以从某个已经存在的类继承。继承的类叫做subclass。比如要构造蜘蛛这个类,可以从昆虫这个类继承。构建学生,雇员,战士都可以从人这个类继承。

多态:因为类具有继承关系,子类可以向上转型被看做是父类的类型,比如无论是战士还是快递员,都是人类。也因为有了继承关系,子类可以继承父类的所有方法和属性,当然也可以重载父类的成员函数及属性。例如,当子类(直升机)和父类(飞机)都存在相同的fly()方法时,子类的fly()覆盖了父类的fly(),在运行时就总是会调用子类的fly()。这就是继承带来的多态。

6. 访问控制

▪︎访问控制
■ Python没有像其它语言有访问控制的关键字,例如 private、protected等等。Python通过命名约定来实 现的访问控制
■ 对模块级的控制,通过在标识符前加单下划线_ 实现。
■ 对类内部的属性及方法,通过在在标识符前加双 下划线__来实现的私有化
■ 类中两个双下划线包裹的属性或方法为特殊方法

  • 在之前Student类的基础上,对其进行数据的保护。便其数据不能再被通过类名.或对象.的形式被调用。
##在之前Student类的基础上,对其进行数据的保护。便其数据不能再被通过类名.或对象.的形式被调用。
david.score=1200
david.score#问题:无需通过考试,即可毕业
#需要访问的控制: __变成私有
#完成一个学生类的设计
#要求能查看总学生人数,全部学生姓名,毕业分数标准1000,已经毕业学员数量。
#实现考试方法,成绩分数如果大于60分代表其通过考试,分数计入该学员总分。
#如果该学员累计分数达到1000分,该学员即毕业
#实现查分方法,向该学员返回是否已经毕业,或者还差多少分数才能毕业
#实现查询所有已毕业学生数量的功能
#实现查询所有学生数量的功能
#实现查询所有学生姓名的功能

class Student():
    student_total=0
    student_list=[]
    student_graduated=0
    pass_score=1000
    
    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
        self.gender=gender
        self.__score=0
        Student.student_total+=1
        Student.student_list.append(name)
        
    def exam(self,score):
        if score<60:
            return 'sorry, You have failed this time,better luck next time'
        else:
            self.__score+=score
            str1='Good!, Your score is now'+str(self.__score)
        
        if self.__score>=Student.pass_score:
            Student.student_graduated+=1
        return str1
    
    def check(self,):
        if self.score<Student.pass_score:
            return 'You have',Student.pass_score-self.score,' scores to archive!'
        else:
            return 'You have graduated from julyedu.com'

    @classmethod
    def get_student_graduated(cls,):
        return Student.student_graduated
    
    @classmethod
    def get_student_list(cls,):
        return Student.student_list    
    
    @staticmethod
    def static1():
        return 'this is a static method from Student, it can be called by both the instance and the class'
    

xiaohong=Student('xiaohong',23,'female')

xm=Student('xiaoming',22,'male')

xm.exam(99)

xiaohong.score=1200
xiaohong.score

7. 魔法方法Magic Method

▪︎魔术方法:
■ 魔法方法就是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那 么这个方法就会在特殊的情况下被 Python 所调用, 你可以定义自己想要的行为,这些会自动发生。
■ 它们经常是两个下划线包围来命名的

  • 构造和初始化魔术
#每个人都知道一个最基本的魔术方法, __init__ 。
#通过此方法我们可以定义一个对象的初始操作。
#然而,当我调用 x = SomeClass() 的时候, __init__ 并不是第一个被调用的方法。
#实际上,还有一个叫做 __new__ 的方法,来构造这个实例。
#然后给在开始创建时候的初始化函数来传递参数。
#在对象生命周期的结束时,也有一个 __del__ 方法。

class myclass():
    def __init__(self,):
        print('被__init__')

    def __del__(self,):
        print('我被__del__了,再见')        

c1=myclass()
del c1
  • 比较魔术:Python对实现对象的比较,使用魔术方法进行了大的逆转,使他们非常直观而不是笨拙的方法调用。而且还提供了一种方法可以重写Python对对象比较的默认行为(通过引用)。以下是这些方法和他们的作用。
# __cmp__(self, other) __cmp__ 是最基本的用于比较的魔术方法。
#它实际上实现了所有的比较符号(<,==,!=,etc.),
#但是它的表现并不会总是如你所愿(比如,当一个实例与另一个实例相等是通过一个规则来判断,
#而一个实例大于另外一个实例是通过另外一个规则来判断)。
#如果 self < other 的话 __cmp__ 应该返回一个负数,当 self == other 的时候会返回0 ,而当 self > other 的时候会返回正数。
#通常最好的一种方式是去分别定义每一个比较符号而不是一次性将他们都定义。
#但是 __cmp__ 方法是你想要实现所有的比较符号而一个保持清楚明白的一个好的方法。

# __eq__(self, other) 定义了等号的行为, == 。

# __ne__(self, other) 定义了不等号的行为, != 。

# __lt__(self, other) 定义了小于号的行为, < 。

# __gt__(self, other) 定义了大于等于号的行为, >= 。

#示例 
class myclass():
    def __init__(self,num):
        self.num=num
        print('被__init__')

    def __del__(self,):
        print('被__del__')   
        
    def __eq__(self,other):
        if type(other)==int:
            return True if self.num>other else False                
        else:
            print('can\'t compare with other datatype except int',)

c1=myclass(3)
c1=='china'
  • 数值处理魔法
# __pos__(self) 实现正号的特性(比如 +some_object)
# __neg__(self) 实现负号的特性(比如 -some_object)
# __abs__(self) 实现内置 abs() 函数的特性。
# __invert__(self) 实现 ~ 符号的特性。
# __add__(self, other) 实现加法。
# __sub__(self, other) 实现减法。 
# __mul__(self, other) 实现乘法。
# __floordiv__(self, other) 实现 // 符号实现的整数除法。
# __div__(self, other) 实现 / 符号实现的除法。
# __truediv__(self, other) 实现真除法。
#__iadd__(self, other) 实现赋值加法+=


class myclass():
    def __init__(self,num):
        self.num=num
        print('被__init__')
        
    def __pos__(self):
        self.num=0

c1=myclass(-3)

+c1

c1.num
#__str__(self) 定义当 str() 调用的时候的返回值
#__repr__(self) 定义 repr() 被调用的时候的返回值。
#__hash__(self) 定义当 hash() 调用的时候的返回值,它返回一个整形,
  • 类表现魔法
class myClass():
    __doc__='this is myClass docuemtnt'# 表示类的描述信息,重写代表自定义描述
    __name__='moduel name is V'#模块是对象,并且所有的模块都有一个内置属性 __name__。
    #一个模块的 __name__ 的值取决于您如何应用模块。如果 import 一个模块,那么模块__name__ 的值通常为模块文件名,不带路径或者文件扩展名。
    #但是您也可以像一个标准的程序样直接运行模块,
    #在这种情况下, __name__ 的值将是一个特别缺省"__main__"。上述类中加上__name__ == '__main__'的判断语句,
    #可以直接在终端环境下执行python dirfile.py /tmp进行测试,不必非得在交互式环境下导入模块进行测试。


    def __init__(self,):
        pass

    def __str__():
        return 'str'
    
    def __repr__():
        return 'repr' 
    
    def __hash__():
        return 7
  • 容器魔法
class FunctionalList():

    def __init__(self, values=None):#初始化
        if values is None:
            self.values = []
        else:
            self.values = values

    def __len__(self):
        return len(self.values)+10#故意多加10个

    def __getitem__(self, key):
        if type(key)==slice:#如果传入的key是一个slice类,则这是一个切片访问
            start=key.start
            stop=key.stop+1
            if key.stop==None:
                stop=-1
            if key.start==None:
                start=0
            return self.values[start:stop]
        else:
            return str(self.values[key])*2#故意多输出2次
    

    def __setitem__(self, key, value):
        self.values[key] = str(value)+'haha'#故意都给转成str,再加haha字样

    def __delitem__(self, key):
        pass
        #del self.values[key]#就是故意不去删

    def __iter__(self):#用于迭代器,之所以列表、字典、元组等可以进行for循环,是因为类型内部定义了 __iter__
#         pass
        return iter(self.values)

    def __reversed__(self):
        return reversed(self.values)


    def append(self, value):#非魔术方法
        self.values.append(value)

l1=list(range(10))
o1=FunctionalList(l1)
len(o1)#调用的是__len__


o1[0:9]
  • 反射
# 你可以通过魔术方法控制控制使用 isinstance() 和 issubclass() 内置方法的反射行为。这些魔术方法是:

# __instancecheck__(self, instance)

# 检查一个实例是不是你定义的类的实例

# __subclasscheck__(self, subclass)

# 检查一个类是不是你定义的类的子类
  • 调用
# 可以调用的对象
# Python中,方法也是一种的对象。这意味着他们也可以被传递到方法中就像其他对象一样。
# __call__可以让类的实例的行为表现的像函数一样,被调用
#将一个函数当做一个参数传到另外一个函数中等等。这是一个非常强大的特性让Python编程更加舒适甜美。 



#魔法函数 __call__(self, [args...])

# __call__允许一个类的实例像函数一样被调用。
# 实质上说,这意味着 x() 与 x.__call__() 是相同的。
# 注意 __call__ 参数可变。这意味着你可以定义 __call__ 为其他你想要的函数,无论有多少个参数。

class callableInstance():

    def __init__(self, x, y):
        self.x, self.y = x, y

    def __call__(self,):
        self.x, self.y = self.y, self.x
        #调用时交换对象
    
c1=callableInstance(1,5)# 执行 __init__,赋值x,y
print(c1.x,c1.y)
c1()# 执行 __call__,交换变量变换
print(c1.x,c1.y)

8. 模块module

▪︎模块定义:
■ 一个.py文件
■ 包含了==对象定义与语句 ==
▪︎模块作用:
■ 用来从逻辑上组织代码
▪︎模块使用:
■ 搜索路径(标准模块,自定义与第三方模块)路径
▢搜索路径设置(修改sys.path(sys.path.append()),设置环境变量)
■ 导入方法

import test#作为模块空间导入
from ** import * #指定模块下具体的类,对象导入,并入当前空间  
from***import***#将模块下所有对象导入,并入当前空间

import sys
sys.path#需在此路径下

9. 包package

▪︎包定义:
■ 含有__init__.py文件夹被称为包, init.py文件用于标识当前文件夹是一个包
(该文件可以为空,但必须有)
包用于组织模块,通常把功能相近的模块进行再次封装成为包。
▪︎包的目录结构:
■ 模块
■ 子包
▢子包下的子包
▪︎包的安装,导入与访问
■ 包的安装(pip,conda)
■ 不同的导入方式(假设包名为test)

import test#导入__init__.py这个moduel。
from test import *#导入__init__.py这个moduel下所有对象导入到当前空间。
from test.test_level1.test_level2 import test_level2#导入的层次目录下的模块。 还是模块

import numpy as np#numpy为包