Python面向对象的理解

python中一切皆为对象,类型的本质就是类!Python常用的数据结构如:List、tuple、dict、array、DataFrame或者Series等等都是类,类的实例就是对象。甚至连fp=open(’…/data/zhou.txt’)的句柄fp都可以作为(变量)对象传入函数。一切就是这么神奇!为了提高代码的复用性和更好的封装函数或变量的作用域,使用类再做一次封装会有用很多。

1.Python中self等价于Java中的this,this只是本类。
2.一个类占有一个独立的空间,类中的属性叫做类变量,类中的函数,叫做类的方法。类(Class):也可以成为类对象。类对象中包含了一批实例对象共有的属性和方法。
3.既然是面向对象,自然满足面向对象的几大特性:抽象、封装、继承、多态(很好的解决了应用程序函数同名问题)
4.面向过程和面向对象:
A.面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
a.优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
b.缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
c.应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

B.面向对象=对象+消息。
a.优点是:面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。
b.缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题

注:个人理解,两者最大的差异是一个通过函数的流水线式直接交互;一个封装成类后,通过类的实例=对象,按照对象.属性或对象.方法来实现交互!实现同一功能时,本质上异曲同工。

例子1:super()继承的使用

Python面向对象编程
# 创建父类学校成员SchoolMember
class SchoolMember:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def tell(self):
        print('Name:"{}",Age:"{}"'.format(self.name,self.age),end=" ")
class Teacher(SchoolMember): #python中的继承机制,这里可以是多继承;java中只能是单继承,但是可以迭代继承多个嵌套封装类
    def __init__(self,name,age,salary):
        SchoolMember.__init__(self,name,age)
        self.salary=salary
    def tell(self):
        SchoolMember.tell(self)
        print('salary:"{}"'.format(self.salary))
class Student(SchoolMember):
    def __init__(self,name,age,score):
        #SchoolMember.__init__(self, name, age)#这里与super()是等价的。
        super().__init__(name, age)
        self.score=score
    def tell(self):
        #SchoolMember.tell(self)
        super().tell()
        print('score: {}'.format(self.score))
   
teacher1 = Teacher("John", 44, "$60000")
student1 = Student("Mary", 12, 99)
teacher1.tell()  # 打印 Name:"John" Age:"44" Salary: $60000
student1.tell()  # Name:"Mary" Age:"12" score: 99

例子2:面向对象中对象的交互操作

#面向对象中对象的交互操作:
class Person:  # 定义一个人类
    role = 'person'  # 人的角色属性都是人
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 每一个角色都有自己的昵称;
        self.aggressivity = aggressivity  # 每一个角色都有自己的攻击力;
        self.life_value = life_value  # 每一个角色都有自己的生命值;
    def attack(self,dog):  
        # 人可以攻击狗,这里的狗也是一个对象。
        # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降
        dog.life_value -= self.aggressivity
        print(dog.__class__,dog.name,'生命值减少',self.aggressivity,'还剩余生命值: ',dog.life_value)

egg = Person('egon',10,1000)
print(egg.name)
print(egg.aggressivity)
print(egg.life_value)
print(egg.attack)
class Dog:  # 定义一个狗类
    role = 'dog'  # 狗的角色属性都是狗
    def __init__(self, name,  aggressivity, life_value):
        self.name = name  # 每一只狗都有自己的昵称;
        self.aggressivity = aggressivity  # 每一只狗都有自己的攻击力;
        self.life_value = life_value  # 每一只狗都有自己的生命值;
    def bite(self,dog):
        # 狗可以咬人,这里的狗也是一个对象。
        # 狗咬人,那么人的生命值就会根据狗的攻击力而下降
        dog.life_value -= self.aggressivity
        print(dog.__class__,dog.name,'生命值减少',self.aggressivity,'还剩余生命值: ',dog.life_value)

dog = Dog('哈士奇',35,1000)  #创造了一只实实在在的狗ha2
print(dog.life_value)         #看看dog的生命值
egg.attack(dog)               #egg打了dog一下
print(dog.life_value)         #dog掉了10点血
#狗咬人一下
print(egg.life_value)         #看看egg的生命值
dog.bite(egg)               #dog打了egg一下
print(egg.life_value) 

例子3:面向对象的组合用法

#面向对象的组合用法
class Weapon:
    def prick(self, obj):  # 这是该装备的主动技能,扎死对方
        obj.life_value -= 500  # 假设攻击力是500
        print('prick后生命力为:',obj.life_value)
class Person:  # 定义一个人类
    role = 'person'  # 人的角色属性都是人
    def __init__(self, name,life_value):
        self.name = name  # 每一个角色都有自己的昵称;
        self.weapon = Weapon()  # 给角色绑定一个武器; 属性值=另一个类对象的实例   
        self.life_value=life_value
egg = Person('egon',1000)
egg.weapon.prick(egg) 
#egg组合了一个武器的对象,可以直接egg.weapon来使用组合类中的所有方法

#基础的对象属性和方法的调用
class Myclass: 
    @classmethod#类装饰器 
    def foo2(self): 
        print(id(self),'foo2') 
print(id(Myclass)) #类对象,直接可以调用,不需要实例化。这句说明了类也是有存储空间的 
Myclass.foo2() #类方法,直接可以调用,不需要实例化对象 

class Myclass:
  @staticmethod#静态方法
  def foo3():   #没有self参数
    print('foo3')
 
Myclass.foo3()   #通过类调用
a=Myclass()
a.foo3()       #通过实例调用

5.public,protected和private类型的规定:
单下划线、双下划线、头尾双下划线说明

实例对象无法访问私有的类变量,但是可以添加同名的私有实例变量。_foo_(): 定义的是特列方法,类似 _init_() 之类的。

_foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *

__foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。

例子1

__all__=["default_age","default","Moniter"] 
# __all__变量,设置导入模块import*时将会自动引入的函数、变量、类 
default_age = 24 #定义模块变量 
def set_default_age(age=26): 
    #定义模块函数 
    print("默认年龄为"+str(age)+"岁") 
class Parent(object): 
    #定义模块类。()内指定基类,当是object可以省略不写 
    print("定义了Student类") #定义类时就会执行内部的执行语句 
    default_name='student' #定义一个类变量(不可修改变量) 
    default_arr = [] #定义一个类变量(可修改变量) 
    __default_age=12 #函数名或属性名前面为双下划线表示成员私有。只能在当前类或对象空间内使用。在存储时是存储成_Parent__default_age 
    def __init__(self, name1='student1',age1=13): 
        #init是构造函数,self代表类的实例对象。参数可以设置默认值。 
        self.name=name1 #自动新增两个实例变量 
        self.age=age1 
        print("基类构造函数设置了"+self.name) 
    def getname(self): #实例方法,函数名为引用变量,可以进行赋值,即变更函数体。函数引用变量调用时使用(),不带括号代表变量。getname代表函数引用变量,getname()代表代表调用函数 
        print('基类读取名称'+self.name) 
        return self.name 
    def setname(self,name1): 
        self.name=name1 
        print("基类设置名称"+self.name) 
    @staticmethod # @staticmethod声明函数为静态方法,通过类对象和实例对象直接调用 
    def info(): #静态方法就像类外函数一样。如果函数内需要读写类变量,需要使用Parent.default_name 
        print("派生类的静态函数"+Parent.default_name) 
    @classmethod # @classmethod声明函数为类方法,第一个参数是能是类对象的引用,可以通过类或者实例直用 
    def setsex(cls): # 类方法self表示对类的引用 
        self.sex = '男' # 添加类变量 
        print("派生类设置性别" + self.sex) # 派生类继承了基类的类变量和类方法和实例方法(没有实例变量,因为实例变量是在实例以后才存在的) 
class Child(Parent): #生成派生类,可以多重继承,但是应尽量避免。继承后会包含基类的函数和特性。 
    def __init__(self,name1="child"): #派生类构造函数不会自动调用基类构造函数 
        self.name = name1 #在当前实例对象中新增name实例变量 
        Parent.__init__(self) #两种方法,调用超类的构造函数。基类中就修改了name实例变量,新增了age实例变量 # super(Child,self).__init__(name1) 
        print("派生类构造函数"+self.name) #这里读取的就是最后一次对name的修改(基类中对他的修改) 
    def setname(self, name1): #重写基类中的方法。其实是在派生类中添加了一个新方法,因此在查找此函数时就必用向上查找了。 
        self.name = "新"+name1 
        print("基类设置名称" + self.name) 
    def getage(self): #派生类添加新的实例方法 
        print("派生类读取年龄"+str(self.__default_age)) #派生类是无法读取基类的私有类变量的。因此这句话会报错 
print('Peopeo类开始运行') #导入模块或执行模块都会执行函数 # 当一个module被执行时,moduel.__name__的值将是"__main__",而当一个 module被其它module引用时,module.__name__将是module自己的名字 
if __name__=="__main__": #只有在执行当前模块时才会运行此函数 
    set_default_age()

6.Python内置方法,本质上也是类及类的实例:

""" import People=将People.py文件导入;等价的写法是将People.py内部的属性和类和方法重新封装成一个总的类,即:People类。"""
class People:
    __all__=["default_age","default","Moniter"] 
    # __all__变量,设置导入模块import*时将会自动引入的函数、变量、类 
    default_age = 24 #定义模块变量 
    def set_default_age(age=26): 
        #定义模块函数 
        print("默认年龄为"+str(age)+"岁") 
    class Parent(object): 
        #定义模块类。()内指定基类,当是object可以省略不写 
        print("定义了Student类") #定义类时就会执行内部的执行语句 
        default_name='student' #定义一个类变量(不可修改变量) 
        default_arr = [] #定义一个类变量(可修改变量) 
        __default_age=12 #函数名或属性名前面为双下划线表示成员私有。只能在当前类或对象空间内使用。在存储时是存储成_Parent__default_age 
        def __init__(self, name1='student1',age1=13): 
            #init是构造函数,self代表类的实例对象。参数可以设置默认值。 
            self.name=name1 #自动新增两个实例变量 
            self.age=age1 
            print("基类构造函数设置了"+self.name) 
        def getname(self): #实例方法,函数名为引用变量,可以进行赋值,即变更函数体。函数引用变量调用时使用(),不带括号代表变量。getname代表函数引用变量,getname()代表代表调用函数 
            print('基类读取名称'+self.name) 
            return self.name 
        def setname(self,name1): 
            self.name=name1 
            print("基类设置名称"+self.name) 
        @staticmethod # @staticmethod声明函数为静态方法,通过类对象和实例对象直接调用 
        def info(): #静态方法就像类外函数一样。如果函数内需要读写类变量,需要使用Parent.default_name 
            print("派生类的静态函数"+Parent.default_name) 
        @classmethod # @classmethod声明函数为类方法,第一个参数是能是类对象的引用,可以通过类或者实例直用 
        def setsex(self): # 类方法self表示对类的引用 
            self.sex = '男' # 添加类变量 
            print("派生类设置性别" + self.sex) # 派生类继承了基类的类变量和类方法和实例方法(没有实例变量,因为实例变量是在实例以后才存在的) 
    class Child(Parent): #生成派生类,可以多重继承,但是应尽量避免。继承后会包含基类的函数和特性。 
        def __init__(self,name1="child"): #派生类构造函数不会自动调用基类构造函数 
            self.name = name1 #在当前实例对象中新增name实例变量 
            Parent.__init__(self) #两种方法,调用超类的构造函数。基类中就修改了name实例变量,新增了age实例变量 # super(Child,self).__init__(name1) 
            print("派生类构造函数"+self.name) #这里读取的就是最后一次对name的修改(基类中对他的修改) 
        def setname(self, name1): #重写基类中的方法。其实是在派生类中添加了一个新方法,因此在查找此函数时就必用向上查找了。 
            self.name = "新"+name1 
            print("基类设置名称" + self.name) 
        def getage(self): #派生类添加新的实例方法 
            print("派生类读取年龄"+str(self.__default_age)) #派生类是无法读取基类的私有类变量的。因此这句话会报错 
    print('Peopeo类开始运行') #导入模块或执行模块都会执行函数 # 当一个module被执行时,moduel.__name__的值将是"__main__",而当一个 module被其它module引用时,module.__name__将是module自己的名字 
    #if __name__=="__main__": #只有在执行当前模块时才会运行此函数 
    #    set_default_age()

# 只有有__init__.py文件的文件夹才能是package,才支持引入
#import People    #引用此模块,就相当于将此模块在此处展开。
#python中一切变量都是引用变量,指向的对象包含不可变量(整型、字符串、元组)和可变量。
# 因此如果修改了不可变量,实质是新开一个空间,修改引用指向。如果修改可变量,就是保持引用指向,在原位置修改数据


# #调用模块变量、模块函数
People.set_default_age(People.default_age)
print('=============实例化===================')
#实例化对象,开辟新内存,保留对类的引用,但是不复制属性和方法。同时调用init初始化,
#因此实例变量可以访问类变量、类方法、静态方法、实例方法。以及自己创建的实例变量。
# 类对象无法访问实例变量。因为并没有一个从类对象到实例对象的指针

#实例变量可以读取的东西:类的公有属性和方法,自身的所有属性和方法。
#实例变量可以修改的东西:类对象的引用变量指向的数据,和自身的所有属性和方法
#实例变量不能修改的东西:类对象的引用变量的指向(包含属性和方法)
parent1 = People.Parent('student1')   #实例化,盗用初始化函数,创建了实例变量name和age
print('基类对象:',People.Parent.__dict__)     #打印对象的自有属性
print('基类实例对象:',parent1.__dict__)    #这一步可以看出实例化没有将类中的属性和方法引用复制到实例对象空间中。
print('==============实例对象读写数据==================')
parent1.default_name   #实例可以访问类变量,因为变量沿原型链的查找
parent1.default_name = 'Student'   #实例无法修改类变量(引用变量)的指向,所以这个是在实例对象中添加了一个实例变量
parent1.default_arr.append(1)   #实例可以修改类变量指向的数据内容
parent1.default_arr = [1,2]    #实例无法修改类变量(引用变量)的指向,所以这个是在实例对象中添加了一个实例变量
# print(parent1.__default_age)   #实例对象不能访问类对象的私有类变量
parent1.__default_age=14     #为实例对象添加实例变量(在执行时添加的不再是私有变量)
print(parent1.__default_age)  #实例对象可以访问到自身的所有属性和方法

print('基类对象:',People.Parent.__dict__)
print('基类实例对象:',parent1.__dict__)
print('=============基类对象读写数据===================')
#类对象可以访问和修改的内容:类的属性和方法
People.Parent.default_name='Student'   #修改类变量的指向
People.Parent.default_arr=[11]          #修改类变量的指向
People.Parent.default_arr.append(12)    #修改类变量指向的内容
People.Parent.__default_age=14          #通过类名无法修改私有类变量,因为在存储中私有变量的存储名为_Parent__default_age。python并不建议修改私有变量,虽然可以通过这个名称修改变量值
print('基类对象:',People.Parent.__dict__)
print('基类实例对象:',parent1.__dict__)

print('=============继承===================')
#派生类,开辟新内存,保留对基类的引用,但是不复制属性和方法。
# 因此派生类对象可以访问基类变量、基类方法、基类静态方法、基类实例方法。以及自己创建的派生类属性和方法。基类对象无法访问派生类对象。因为并没有一个从基类对象到派生类对象的指针

#派生类对象可以读取的东西:基类的公有和保护属性和方法,自身的所有属性和方法。
#派生类对象可以修改的东西:基类对象的引用变量指向的数据,和自身的所有属性和方法
#派生类不能修改的东西:基类对象的引用变量的指向(包含属性和方法)

print('基类对象:',People.Parent.__dict__)     #打印对象的自有属性
print('派生类对象:',People.Child.__dict__)    #这一步可以看出实例化没有将类中的属性和方法引用复制到实例对象空间中。
print('=============派生类对象读写数据===================')
People.Child.default_name   #派生类对象可以访问基类的类变量,因为变量沿原型链的查找
People.Child.default_name = 'child'   #派生类对象无法修改基类的类变量(引用变量)的指向,所以这个是在派生类对象中添加了一个类变量
People.Child.default_arr.append(1)   #派生类对象可以修改基类的类变量指向的数据内容
People.Child.default_arr = [1,2]    #派生类对象无法修改基类的类变量(引用变量)的指向,所以这个是在派生类对象中添加了一个类变量
# print(People.Child.__default_age)   #派生类对象不能访问基类对象的私有类变量
People.Child.__default_age=14     #为派生类对象添加类变量(在执行时添加的不再是私有变量)
print(People.Child.__default_age)  #类对象可以访问到自身的所有属性和方法

print('基类对象:',People.Parent.__dict__)
print('派生类对象:',People.Child.__dict__)
print('=============派生类的实例对象读写数据===================')
#基类的实例对象和基类的关系等同于派生类的实例对象和派生类的关系
# 派生类的初始化函数调用不会自动调用基类的初始化函数
child1 = People.Child()  #调用类的初始化函数,实例化一个对象,这里在派生类的实例对象中添加了name和age属性
child1.setname('child')  #调用派生类对象中的重写或继承的方法
# child1.getage()  #调用派生类对象添加的方法,方法访问了基类的私有变量,会报错
print('派生类对象:',People.Child.__dict__)
print('派生类实例对象:',child1.__dict__)

print('==============静态方法==================')
parent1.info()         #调用静态函数,方法1
People.Parent.info()   #调用静态函数,方法2
print('=============类方法===================')
# print(parent1.sex)   #类对象和实例对象中不存在sex,所以无法查找到,访问出错
parent1.setsex()    #调用类方法,在类对象中添加类变量
print(parent1.sex)  #通过实例对象访问类变量

print('基类对象:',People.Parent.__dict__)
print('基类实例1对象:',parent1.__dict__)


print('===========原型链查询=====================')
print(issubclass(People.Child,People.Parent)) # 布尔函数(Child,Parent)判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
print(isinstance(child1, People.Parent))  #布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

注:针对博客里面很多代码直接粘贴会大变样,建议:使用右键>查看网页源代码>发现代码格式被很好的保存下来了。

猜你喜欢

转载自blog.csdn.net/jp_zhou256/article/details/85558175