python基础--面向对象基础(类与对象、对象之间的交互和组合、面向对象的命名空间、面向对象的三大特性等)

python基础--面向对象

(1)面向过程VS面向对象

面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。

优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。

应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。

面向对象的程序设计的

优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题即便是上帝也无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致阴霸的技能出现,一刀砍死3个人,这个游戏就失去平衡。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

在python 中面向对象的程序设计并不是全部。

面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

了解一些名词:类、对象、实例、实例化

类:具有相同特征的一类事物(人、狗、老虎),先来定义一个模子,用来描述一类事物,具体相同的属性和动作

对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)

实例化:类——>对象的过程

类是一个大范围,是抽象的,是一个模子,它约束了事物有哪些属性 但是不能约束具体的值

对象是一个具体的内容,是模子的产物,它遵循了类的约束 同时给属性赋上了具体的值。

Person是一个类,zhouqian和AndreasZhou都是这个类的对象

(2)类和对象

python中一切皆为对象,类型的本质就是类,所以,不管你信不信,你已经使用了很长时间的类了。例如我们学过的list(列表)、tuple(元组)、dict(字典)、set(集合)等等这些都是类。我们在使用的时候,本质上就是一个实例化对象的过程。

>>> dict #类型dict就是类dict
<class 'dict'>
>>> d=dict(name='eva') #实例化
>>> d.pop('name') #向d发一条消息,执行d的方法pop
'eva'

在python中,用变量表示特征,用函数表示技能,因而具有相同特征和技能的一类事物就是‘类’,对象是则是这一类事物中具体的一个。

我们先写一个类的例子:

class Person: # Person 类名
    def __init__(self): # 这个函数也是执行的
        # 这个方法必须叫__init__这个名字,不能够改变的,所有的在一个具体的人物出现之后拥有的属性都可以写在这里
        # 这个函数里面的代码,在调用的时候才会执行的。不会在创建类的时候就执行。而类里面的代码是在创建了类就会执行。
        print('-'*20)
        self.name = 'alex'
        self.sex = '不详'
        self.job = '搓澡工'
        self.level = 0
        self.hp = 250
        self.weapon = '搓澡巾'
        self.ad = 1
        print('*' * 20)
        print(self,self.__dict__) # <__main__.Person object at 0x000002AFD568A588> {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
        print('in person') # 这个代码是执行的。


# 实例化的一个过程,这里的zhouqian和类里面的self是指同一个一样的东西。
alex = Person()  # 类名() 会自动调用类中的__init__()方法  alex就是对象  alex = Person()的过程是通过类获取一个对象的过程--实例化
print(zhouqian.__dict__) # {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
print(zhouqian, zhouqian.__dict__) # <__main__.Person object at 0x000002AFD568A588> {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}

'''
输出的结果为:
--------------------
********************
<__main__.Person object at 0x0000029C89F59808> {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
in person
{'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
<__main__.Person object at 0x0000029C89F59808> {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
'''

在这里我们进行一个知识点的总结:

1.类的定义,我们是用到了关键字class,就和我们函数定义用到的关键字def一样,定义的类型如下所示。

class 类名(父类名):
    '类的文档字符串'
    类体
 
这里的类体包括方法(函数)和静态属性
在实际的过程中,很多人分不清楚什么是函数,什么是方法?其实方法和函数本质上没有区别,主要的区别为:函数:如果是类名调用,那么我们就叫函数。方法:如果是实例化对象调用,那么我们就叫做方法。

2.属性(变量)有两种一个是静态属性(静态变量)和实例化属性(实例化变量)

1)静态属性(静态变量)的引用:

class Person:   #定义一个人类
    role = 'person'  #人的角色属性都是人
    def walk(self):  #人都可以走路,也就是有一个走路方法
        print("person is walking...")

print(Person.role)  #查看人的role属性

2)实例化属性(实例化变量)的引用:

实例化的过程就是类——>对象的过程,实例化对象和self指向的是同一个内存地址的空间,可以理解为是同一个。

原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。

语法:对象名 = 类名(参数)

# 先进行实例化
egg = Person('egon')  #类名()就等于在执行Person.__init__()
#执行完__init__()就会返回一个对象。这个对象类似一个字典,存着属于这个人本身的一些属性和方法。
print(egg.name)     #查看属性直接 对象名.属性名

3.__init__方法的含义

类名() 会自动调用类中的__init__方法

实例化:类名加括号就是实例化,会自动触发init函数的运行,可以用它来为每个实例定制自己的特征。这个__init__方法必须是这个名字,不能够改变的,将实例化变量(实例化属性)与self绑定在一起,也就是和对象绑定在一起。

我们再看一个上面的例子升级版--面向对象的语法基础、练习类的创建和实例化

class Person:  # Person 类名
    country = 'China'
    def __init__(self, name, sex, job, level, hp, weapon, ad):  # 这个函数也是执行的
        # 这个方法必须叫__init__这个名字,不能够改变的,所有的在一个具体的人物出现之后拥有的属性
        # 都可以写在这里
        # 这个函数里面的代码,在调用的时候才会执行的。不会在创建类的时候就执行。
        print('-' * 20)
        self.name = name
        self.sex = sex
        self.job = job
        self.level = level
        self.hp = hp
        self.weapon = weapon
        self.ad = ad
        print('*' * 20)
        print(self,self.__dict__)  # <__main__.Person object at 0x000002AFD568A588> {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'levle': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}

# zhouqian和self是一个东西,指向的是一个东西
zhouqian = Person('zhouqian', '不详', '搓澡工', 0, 250, '搓澡巾', 1)
print(zhouqian)
print(zhouqian.__dict__)
'''
输出的结果如下:
--------------------
********************
<__main__.Person object at 0x0000018B63CFD208> {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
<__main__.Person object at 0x0000018B63CFD208>
{'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
'''
AndreasZhou = Person('AndreasZhou', 'male', '法师', 100, 500, '打狗棍', 1000)
print(AndreasZhou, AndreasZhou.__dict__)
print(zhouqian, zhouqian.__dict__)
'''
输出的结果为:
--------------------
********************
<__main__.Person object at 0x000001AC3AC58688> {'name': 'AndreasZhou', 'sex': 'male', 'job': '法师', 'level': 100, 'hp': 500, 'weapon': '打狗棍', 'ad': 1000}
<__main__.Person object at 0x000001AC3AC58688> {'name': 'AndreasZhou', 'sex': 'male', 'job': '法师', 'level': 100, 'hp': 500, 'weapon': '打狗棍', 'ad': 1000}
<__main__.Person object at 0x000001AC3AC44F08> {'name': 'zhouqian', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}
'''

# Person是一个类,zhouqian和AndreasZhou都是这个类的对象
# 类有一个空间,存储的是定义在class中的所有名字
# 每一个对象又拥有自己的空间,通过对象名.__dict__就可以查看到这个对象的属性和值
print(zhouqian, zhouqian.__dict__['name'])  # <__main__.Person object at 0x0000022D7F7E4F88> alex
# 通过上面这个方法,我们可以获取到字典中的键值所对应的值。
# 但是上面的获取步骤有点麻烦,我们的面向对象给了我们一种方法,更加简单的获取到字典的键值所对应的值。
print(zhouqian, zhouqian.name)  # <__main__.Person object at 0x000001FFBC9F4F88> alex
# 直接通过上面的方法取到值,这个值是表示属性的值查看。通过实例化对象zhouqian.属性name,就可以获取到相应的值。

# 同时,我们也可以进行属性值的修改。
zhouqian.name = 'zhouqiansb'  # 属性的修改
print(zhouqian.name)  # alexsb

# 同时,我们也可以增加属性
zhouqian.money = 100  # 属性的增加
print(zhouqian.__dict__, zhouqian.money)
# {'name': 'zhouqiansb', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1, 'money': 100} 100

# 属性的删除
del zhouqian.money  # 属性的删除
print(zhouqian.__dict__)
# {'name': 'zhouqiansb', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 250, 'weapon': '搓澡巾', 'ad': 1}

print(Person.country) # China
Person.country = 'dasd'
print(Person.country) # dasd
del Person.country
print(Person.country) # type object 'Person' has no attribute 'country'
Person.name = 'zq'
print(Person.name) # zq
print(Person.__dict__) # {'__module__': '__main__', '__init__': <function Person.__init__ at 0x0000020BDE085EE8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'name': 'zq'}

我们这里再来一个类的例子,这个例子说的是在对象中定义方法和调用方法

class Person():  # Person 类名
    def __init__(self, name, sex, job, level, hp, weapon, ad):
        self.name = name  # 对象属性/实例变量
        self.sex = sex
        self.job = job
        self.level = level
        self.hp = hp
        self.weapon = weapon
        self.ad = ad
        
    def cuo(self):  # 方法,又是一个必须传的参数----》self对象,定义方法的时候self就会作为参数自动的传入
        print('wahaha', self)
        # wahaha <__main__.Person object at 0x0000017FF16F95C8>
        # 所以我们就可以获取到每一个属性,或者去操作每一个属性,用我们定义的函数去操作每一个属性的值
        print('wahaha', self.name)  # wahaha alex
        print('wahaha', self.name, self.hp, self.ad)  # wahaha alex 250 1


alex = Person('alex', '不详', '搓澡工', 0, 250, '搓澡巾', 1)  # 对象\实例  类名()---》实例化过程
alex.cuo()  
print(alex)  # <__main__.Person object at 0x0000017FF16F95C8>   self的地址和alex的地址是一模一样的,指向的是同一个事物,在实际的开发的过程中,如果实在不知道具体是什么逻辑,就直接认为self和实例化对象是一样的。


class Dog():  # Person 类名
    def __init__(self, name, blood, aggr, kind):
        self.dog_name = name
        self.hp = blood
        self.ad = aggr
        self.kind = kind

    def tiao(self):
        print(self.__dict__)


littleDog = Dog('小白', 5000, 249, '萨摩耶')
littleDog.tiao() # {'dog_name': '小白', 'hp': 5000, 'ad': 249, 'kind': '萨摩耶'}

littleDog1 = Dog('小金', 6000, 266, '金毛')
littleDog1.tiao() # {'dog_name': '小金', 'hp': 6000, 'ad': 266, 'kind': '金毛'}

在这里我们进行一个知识点的总结:

  1. zhouqian = Person(),,如果实例化对象,我们没有传入参数的时候,这个时候,我们会报错。报错的信息如下:init() takes 1 positional argument but 8 were given

  2. 那么这些实际参数应该怎么传入到这个类中呢?

    首先,我们要知道,在实例化的时候,我们在Person()中传入的参数,会被放到__init__方法中。

    所以我们要接受这些参数,我们要在__init__方法中接受这些参数。将实际参数的值和对象(self)绑定在一起。

  3. 静态变量(静态属性)和实例化变量(实例化属性)的值可以进行修改、增加、删除、增加

  4. 类中方法的调用:类.函数名() 对象。方法名()

  5. 类属性的补充

    一:我们定义的类的属性到底存到哪里了?有两种方式查看
    dir(类名):查出的是一个名字列表
    类名.__dict__:查出的是一个字典,key为属性名,value为属性值
    
    二:特殊的类属性
    类名.__name__# 类的名字(字符串)
    类名.__doc__# 类的文档字符串
    类名.__base__# 类的第一个父类(在讲继承时会讲)
    类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
    类名.__dict__# 类的字典属性
    类名.__module__# 类定义所在的模块  __main__
    类名.__class__# 实例对应的类(仅新式类中) # <class 'type'>
    
  6. python是一切皆对象,修改列表、字典、中的某个值,或者是对象的某一个属性,都不会影响这个对象、字典、列表所在的内存空间。

  7. 实例化所经历的步骤

    1.类名() 之后的第一件事儿:开辟一块儿内存空间

    2.调用__init__方法,把空间的内存地址作为self参数传递到函数内部

    3.所有的这一个对象需要使用的属性都需要和self关系起来

    4.执行完init中的逻辑之后,self变量会自动的被返回到调用处(发生实例化的地方)----self和实例化对象指向的是同一个

  8. 定义和调用的固有模式

    class 类名:
        def __init__(self,参数1,参数2):
            self.对象的属性1 = 参数1
            self.对象的属性2 = 参数2
    
        def 方法名(self):pass
    
        def 方法名2(self):pass
    
    对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西
                      #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
                      #括号里传参数,参数不需要传self,其他与init中的形参一一对应
                      #结果返回一个对象
    对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可
    对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可
    类名.静态属性(静态变量)
    

(3)对象之间的交互(注重在行为)

我们这里来一个类的例子,实现对象之间的交互。

class Person():  # Person 类名
    def __init__(self, name, sex, job, level, hp, weapon, ad):
        self.name = name  # 对象属性/实例变量
        self.sex = sex
        self.job = job
        self.level = level
        self.hp = hp
        self.weapon = weapon
        self.ad = ad

	def cuo(self, dog):
		dog.hp -= self.ad
		print('%s 攻击了 %s,%s掉了%s点血' % (self.name, dog.dog_name, dog.dog_name, self.ad))

class Dog():  # Dog 类名
    def __init__(self, name, blood, aggr, kind):
        self.dog_name = name
        self.hp = blood
        self.ad = aggr
        self.kind = kind

	 def tiao(self, person):
		person.hp -= self.ad
		print('%s 攻击了 %s,%s掉了%s点血' % (self.dog_name, person.name, person.name, self.ad))

zhouqian = Person('zhouqian', '不详', '搓澡工', 0, 250, '搓澡巾', 1)  # 对象\实例  类名()---》实例化过程
littleDog = Dog('小白', 5000, 249, '萨摩耶')
littleDog1 = Dog('小金', 6000, 266, '金毛')

zhouqian.cuo(littleDog)
zhouqian.cuo(littleDog1)
littleDog.tiao(zhouqian)
littleDog1.tiao(zhouqian)
'''
输出的结果为:
zhouqian 攻击了 小白,小白掉了1点血
zhouqian 攻击了 小金,小金掉了1点血
小白 攻击了 zhouqian,zhouqian掉了249点血
小金 攻击了 zhouqian,zhouqian掉了266点血
'''

接下来我们用对象的交互实现一个简单的小游戏

定义一个人类

class Person:  # 定义一个人类
    role = 'person'  # 人的角色属性都是人

    def __init__(self, name, aggressivity, life_value, money):
        self.name = name  # 每一个角色都有自己的昵称;
        self.aggressivity = aggressivity  # 每一个角色都有自己的攻击力;
        self.life_value = life_value  # 每一个角色都有自己的生命值;
        self.money = money

    def attack(self,dog):
        # 人可以攻击狗,这里的狗也是一个对象。
        # 人攻击狗,那么狗的生命值就会根据人的攻击力而下降        dog.life_value -= self.aggressivity

定义一个狗类

class Dog:  # 定义一个狗类
    role = 'dog'  # 狗的角色属性都是狗

    def __init__(self, name, breed, aggressivity, life_value):
        self.name = name  # 每一只狗都有自己的昵称;
        self.breed = breed  # 每一只狗都有自己的品种;
        self.aggressivity = aggressivity  # 每一只狗都有自己的攻击力;
        self.life_value = life_value  # 每一只狗都有自己的生命值;

    def bite(self,people):
        # 狗可以咬人,这里的狗也是一个对象。
        # 狗咬人,那么人的生命值就会根据狗的攻击力而下降
        people.life_value -= self.aggressivity

接下来,又创建一个新的兵器类。

class Weapon:
    def __init__(self,name, price, aggrev, life_value):
        self.name = name
        self.price = price
        self.aggrev = aggrev
        self.life_value = life_value

    def update(self, obj):  #obj就是要带这个装备的人
        obj.money -= self.price  # 用这个武器的人花钱买所以对应的钱要减少
        obj.aggressivity += self.aggrev  # 带上这个装备可以让人增加攻击
        obj.life_value += self.life_value  # 带上这个装备可以让人增加生命值

    def prick(self, obj):  # 这是该装备的主动技能,扎死对方
        obj.life_value -= 500  # 假设攻击力是500

测试交互

lance = Weapon('长矛',200,6,100)
egg = Person('egon',10,1000,600)  #创造了一个实实在在的人egg
ha2 = Dog('二愣子','哈士奇',10,1000)  #创造了一只实实在在的狗ha2

#egg独自力战"二愣子"深感吃力,决定穷毕生积蓄买一把武器
if egg.money > lance.price: #如果egg的钱比装备的价格多,可以买一把长矛
    lance.update(egg) #egg花钱买了一个长矛防身,且自身属性得到了提高
    egg.weapon = lance #egg装备上了长矛,带上装备

print(egg.money,egg.life_value,egg.aggressivity)

print(ha2.life_value)
egg.attack(ha2)   #egg打了ha2一下
print(ha2.life_value)
egg.weapon.prick(ha2) #发动武器技能
print(ha2.life_value) #ha2不敌狡猾的人类用武器取胜,血槽空了一半

按照这种思路一点一点的设计类和对象,最终你完全可以实现一个对战类游戏。

(4)面向对象的命名空间(重点)

我们先看一个例子:

class A:
    Country = '中国'  # 静态变量/静态属性
    # 存储在类的命名空间里面的,可以通过类名.变量取到  也可以实例化对象之后,通过实例化对象.变量取到
    def __init__(self):
        # 绑定方法,存储在类的命名空间里面的,可以通过类名.函数名调用
        # 但是在实际的开发的过程中,我们都是不会用类去调用方法,而是通过实例化对象去调用类里面的方法。
        pass
    def fun1(self):
        print(self) # <__main__.A object at 0x000001B320899B88>
    def fun2(self):
        pass
    def fun3(self):
        pass
    def fun4(self):
        pass
    def fun5(self):
        pass
    Country = '美国'
    print(Country) # 美国
print(A.__dict__)
# {'__module__': '__main__', 'Country': '中国', '__init__': <function A.__init__ at 0x0000015BA6EBB828>,
# 'fun1': <function A.fun1 at 0x0000015BA6EBB3A8>, 'fun2': <function A.fun2 at 0x0000015BA6EBB4C8>, 'fun3': <function
# A.fun3 at 0x0000015BA6EBB5E8>, 'fun4': <function A.fun4 at 0x0000015BA6EBB678>, 'fun5': <function A.fun5 at
# 0x0000015BA6EBB708>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of
# 'A' objects>, '__doc__': None}

# {'__module__': '__main__', 'Country': '美国', '__init__': <function A.__init__ at 0x0000015BA6EBB828>,
# 'fun1': <function A.fun1 at 0x0000015BA6EBB3A8>, 'fun2': <function A.fun2 at 0x0000015BA6EBB4C8>, 'fun3': <function
# A.fun3 at 0x0000015BA6EBB5E8>, 'fun4': <function A.fun4 at 0x0000015BA6EBB678>, 'fun5': <function A.fun5 at
# 0x0000015BA6EBB708>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of
# 'A' objects>, '__doc__': None}

# a = A()
# print(a.Country)

print(A.Country) # 美国
# print(A.__init__())  __init__() missing 1 required positional argument: 'self'
print(A.__init__) # <function A.__init__ at 0x000001C52A64B828>
# 类的加载顺序是从上到下一次执行的,安装顺序执行的。
a = A()
print(A.Country)
a.fun1()  #==== A.func1(a)

'''
输出的结果为:
美国
{'__module__': '__main__', 'Country': '美国', '__init__': <function A.__init__ at 0x0000025058C5C828>, 'fun1': <function A.fun1 at 0x0000025058C5C3A8>, 'fun2': <function A.fun2 at 0x0000025058C5C4C8>, 'fun3': <function A.fun3 at 0x0000025058C5C5E8>, 'fun4': <function A.fun4 at 0x0000025058C5C678>, 'fun5': <function A.fun5 at 0x0000025058C5C708>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
美国
<function A.__init__ at 0x0000025058C5C828>
美国
<__main__.A object at 0x0000025058C69D48>
'''

这个例子的类的命名空间如下所示。系统会为类创建一个命名空间,存了如下的属性和函数。

我们在举一个难度大的例子,这个例子设计到继承的知识。

class Animal(object):
    kind = 'cute'
    def __init__(self, name, sex, height, weight):
        self.name = name
        self.sex = sex
        self.height = height
        self.weight = weight
    def func(self):
        print(self.name, self.sex, self.height, self.weight)
class Dog(Animal):
    def __init__(self, name, sex, height, weight, hobby, food):
        Animal.__init__(self, name, sex, height, weight)
        self.hobby = hobby
        self.food = food
    def func1(self):
        print(self.name, self.sex, self.height, self.weight, self.hobby, self.food)
        print(self.kind)
class Cat(Animal):
    def __init__(self, name, sex, height, weight, hobby, food):
        Animal.__init__(self, name, sex, height, weight)
        self.hobby = hobby
        self.food = food
    def func2(self):
        print(self.name, self.sex, self.height, self.weight, self.hobby, self.food)
        self.kind = 'ugly'
        print(Animal.kind)
dog = Dog('小狗', 'male', 188, 125, '喝', '猫粮')
cat = Cat('小猫', 'male', 178, 105, '玩', '猫粮')
dog.func1()
cat.func2()
dog.func()
cat.func()
print(Animal.__dict__)
'''
输出的结果为:
小狗 male 188 125 喝 猫粮
cute
小猫 male 178 105 玩 猫粮
cute
小狗 male 188 125
小猫 male 178 105
{'__module__': '__main__', 'kind': 'cute', '__init__': <function Animal.__init__ at 0x000001C50873C4C8>, 'func': <function Animal.func at 0x000001C50873C5E8>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
'''

在这里用语言总结一下,创建类的时候,会分配相应的类的命名空间。在实例化对象的时候,也会分配相应实例化对象的命名空间。我们首先要弄清楚每一个命名空间里面都是存了什么。在实例化对象中调用属性和方法的时候,我们都是先从实例化对象开始找,找到就是我们要的。如果没有找到就通过类指针继续向类中找,类中如果找到了就是我们要的。如果没有被找到,我们继续想父类找。。。。以此类推。。

在这里我们对命名空间的知识做一个总结:

1.类中的变量是静态变量/静态属性,对象中的变量是属于是实例变量/实例属性。

2.对象中的变量只属于对象本身,每个对象有属于自己的空间来存储对象的变量,当使用对象名去调用某一个属性的时候会优先在自己的空间中寻找,找不到再去对应的类中寻找。如果自己没有就会去引用类的,如果类也没有那么就会报错。对于类来说,类中的变量所有的对象都是可以读取的,并且读取的是同一份变量

3.类中的静态变量的用处:如果一个变量是所有的对象共享的值,那么这个变量应该被定义成静态变量。所有和静态变量相关的增删改查都应该使用类名来处理,而不应该使用对象名直接修改静态变量。因为会在对象的命名空间中创建一个变量。这样的话就失去了静态变量的意义。

(5)对象的组合(注重在属性)

在学习组合之前,我们先了解一下组合这个概念。组合其实是:一个类的对象是另外一个类对象的属性。这个就是我们说的组合。对象变成了另外一个对象的属性,这个就是组合

在这里我们举一个组合的例子:班级类的对象为学生类对象的一个属性,这个例子的应用就是组合

1.学生信息类:姓名、性别、年龄、学号、班级、手机号

2.班级信息类:班级名称、开班时间、当前讲师

问:我们怎么取到姓名为zhouqian的班级信息,怎么取到姓名为zhouqian的班级名称,怎么取到姓名为zhouqian的班级开班时间、怎么取到姓名为zhouqian的班级当前讲师?我们实现的代码如下所示。

# 学生类
    # 姓名 性别 年龄 学号 班级 手机号
class Student:
    def __init__(self, name, sex, age, number, cls, phone):
        self.name = name
        self.sex = sex
        self.age = age
        self.number = number
        self.cls = cls
        self.phone = phone
# 班级信息
    # 班级名称
    # 开班时间
    # 当前讲师
class Cls:
    def __init__(self, cname, begin, teacher):
        self.cname = cname
        self.begin = begin
        self.teacher = teacher
py22 = Cls('python全栈22期', '2019-4-26', '小白')
py23 = Cls('python全栈23期', '2019-5-28', '宝元')
student = Student('大壮', 'male', 18, 27, py23, '13912012012')
student1 = Student('雪飞', 'female', 18, 17, py22, '13912012013')
print(student.cls, py23)
# <__main__.Cls object at 0x000002E529888A48> <__main__.Cls object at 0x000002E529888A48>
print(py23.begin)  # 2019-5-28
# 查看student(大壮)所在的班级cls的开班日期是多少?
print(student.cls.begin) # 2019-5-28
# 查看student(雪飞)所在的班级cls的开班日期是多少?
print(student1.cls.begin) # 2019-4-26

在这里我们在举一个组合的例子:

1.班级信息类:班级名称、开班时间、当前讲师、包含一个属性--课程

2.课程信息类:课程名称、课程周期、课程价格

创建两个班级linux57和python22

查看linux57期的班级所学课程的价格

查看python22期的班级所学课程的周期

class cls:
    def __init__(self,cname,begin,teacher,course):
        self.cname = cname
        self.begin = begin
        self.teacher = teacher
        self.course = course
class course:
    def __init__(self,csname,cstime,csprice):
        self.csname = csname
        self.cstime = cstime
        self.csprice = csprice

linux = course('linux','一个月',10000)
python = course('python','两个月',20000)
linux57 = cls('linux57','2019-1-1','zhouqian',linux)
python22 = cls('python','2019-4-26','andreas',python)

# 查看linux57期的班级所学课程的价格
print(linux57.course.csprice)
# 查看python22期的班级所学课程的周期
print(python22.course.cstime)
'''
输出的结果为:
10000
两个月
'''

在对象的组合结束之后,我们在这里普及一个坑,这个坑很多人都会犯错。仔细观察下面两个程序。

# 程序一:
class A:
    Country = '中国'
    def __init__(self,name,age,country):
        self.name = name
        self.age = age
    def Country(self):
        return self.Country
a = A('alex',23,'印度')
b = A('wusir',74,'泰国')
print(a.Country)
print(a.Country())
# <bound method A.Country of <__main__.A object at 0x000001965F6FB2C8>>
# <bound method A.Country of <__main__.A object at 0x000001965F6FB2C8>>

# 程序二:
class A:
    Country = '中国'
    def __init__(self,name,age,country):
        self.name = name
        self.age = age
    def Country1(self):
        return self.Country
a = A('alex',23,'印度')
b = A('wusir',74,'泰国')
print(a.Country)
print(a.Country1())
'''
中国
中国
'''

(6)面向对象的三大特性(继承、多态、封装)

1)继承的基础知识

在讲解类的继承之前,我们先做一个例子来体验一下继承的特别之处。

1.猫类:名字、吃、喝、睡、爬树

2.狗类:名字、吃、喝、睡、看家

我们没有使用类的继承的时候,我们写的代码如下所示。

class Cat:
    def __init__(self,name):
        self.name = name
    def eat(self):
        print(f'{self.name} is eating!')
        # print('%s is eating!' %self.name)
        # print('{0} is eating!'.format(self.name))
    def drink(self):
        print(f'{self.name} is drinking')
    def sleep(self):
        print(f'{self.name} is sleeping!')
    def climb_tree(self):
        print(f'{self.name} is climbing tree')

class Dog:
    def __init__(self,name):
        self.name = name
    def eat(self):
        print(f'{self.name} is eating!')
        # print('%s is eating!' %self.name)
        # print('{0} is eating!'.format(self.name))
    def drink(self):
        print(f'{self.name} is drinking')
    def sleep(self):
        print(f'{self.name} is sleeping!')
    def house_keep(self):
        print(f'{self.name} is house keeping')

cat = Cat('布偶')
cat.eat()  # Cat.eat(cat)
cat.drink()
cat.sleep()
cat.climb_tree()
dog = Dog('萨摩耶')
dog.eat()
dog.drink()
dog.sleep()
dog.house_keep()
'''
输出的结果为:
布偶 is eating!
布偶 is drinking
布偶 is sleeping!
布偶 is climbing tree
萨摩耶 is eating!
萨摩耶 is drinking
萨摩耶 is sleeping!
萨摩耶 is house keeping
'''

上面的代码看起来是十分的累赘,有很多重复的代码,那么我们怎么解决代码的重复的问题呢?这里是我们需要思考的一个问题。需要解决代码的重复问题,使得代码更加的简洁。要解决这个问题,我们需要用到继承的知识才可以解决,所以我们在这里引入了继承的知识。

继承------需要解决代码的重复问题。继承的语法如下所示。

# 单继承
class A:
    pass
class B(A):
    pass
# 多继承
class C:
    pass
class D:
    pass
class E(C,D):
    pass

表示的是B类继承了A类。称为A是父类,B是子类。A也可以叫父类、基类、超类。B是子类、派生类。

在这里我们使用继承的知识来写上面的代码。

# 子类可以使用父类中的:方法和静态变量(静态属性)
# 当子类和父类的方法重名的时候,我们只使用子类的方法,而不会去调用父类的方法了。(在java语言中,这个叫做方法的重写)
# 子类可以使用父类中的方法,但是也可以重写父类中的方法,同时也可以使用静态变量
class Animal:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f'{self.name} is eating!')
        # print('%s is eating!' %self.name)
        # print('{0} is eating!'.format(self.name))

    def drink(self):
        print(f'{self.name} is drinking')

    def sleep(self):
        print(f'{self.name} is sleeping!')

class Cat(Animal):
    def eat(self):
        print('%s 吃猫粮'%self.name)
    def climb_tree(self):
        print(f'{self.name} is climbing tree')

class Dog(Animal):
    def house_keep(self):
        print(f'{self.name} is house keeping')

cat = Cat('布偶')
cat.eat()  # Cat.eat(cat)  如果子类也有eat方法,那么输出的结果为:布偶 吃猫粮
cat.drink()
cat.sleep()
cat.climb_tree()
dog = Dog('萨摩耶')
dog.eat()
dog.drink()
dog.sleep()
dog.house_keep()
# 有继承的知识可知,我们写的代码变得更加的整洁,没有重复代码,使得我们写的代码不会冗余。
'''
继承的输出后的结果为:
布偶 吃猫粮
布偶 is drinking
布偶 is sleeping!
布偶 is climbing tree
萨摩耶 is eating!
萨摩耶 is drinking
萨摩耶 is sleeping!
萨摩耶 is house keeping
'''
# 先开辟空间,空间里有一个类指针--》指向类(Cat)的命名空间
# self指向这一块空间。调用init方法,对象在自己开辟的空间中
# 找init方法没有找到,那么就会去类(Cat)的命名空间中找init方法,但是还是
# 没有找到,那么就会去父类(Animal)中找init方法。
# 继承的话,也是有一个类指针指向父类。这样就可以找到父类的方法和静态变量(静态属性)

我们进一步的升级自己的代码。

# 猫和狗吃的东西我们完全可以通过传入参数来实现。
# 子类想要调用父类方法的同时还想执行自己的方法
# 猫和狗在调用eat方法的时候调用自己的eat方法,也调用父类的Animal的eat方法
# 在子类中调用父类的方法:父类名.方法名(self)  这里要主动传入self参数
class Animal:
    def __init__(self, name,food):
        self.name = name
        self.food = food
        self.blood = 100
        self.wise = 100
    def eat(self):
        print(f'{self.name} is eating {self.food}!')

    def drink(self):
        print(f'{self.name} is drinking')

    def sleep(self):
        print(f'{self.name} is sleeping!')

class Cat(Animal):
    def eat(self):
        self.blood += 100
        Animal.eat(self) # 这是一个实现子类想要调用父类方法的同时还想执行自己的方法的关键代码
    def climb_tree(self):
        print(f'{self.name} is climbing tree')
        # Animal.drink(self) 这两个方法都可以 一样
        # self.drink()

class Dog(Animal):
    def eat(self):
        self.wise += 100
        Animal.eat(self)
    def climb_tree(self):
        print(f'{self.name} is house keeping')

cat = Cat('布偶','猫粮')
dog = Dog('萨摩耶','狗粮')
cat.eat()
dog.eat()
cat.climb_tree()
'''
布偶 is eating 猫粮!
萨摩耶 is eating 狗粮!
布偶 is climbing tree
'''
print(cat.__dict__)
print(dog.__dict__)
'''
输出的结果为:
布偶 is eating 猫粮!
萨摩耶 is eating 狗粮!
布偶 is climbing tree
{'name': '布偶', 'food': '猫粮', 'blood': 200, 'wise': 100}
{'name': '萨摩耶', 'food': '狗粮', 'blood': 100, 'wise': 200}
'''

我们用继承的知识来做下面的一个题目:

class Foo:
    def __init__(self):
        self.func()
    def func(self):
        print('in foo')

class Son(Foo):
    def func(self):
        print('in son')

Son() # 输出的结果为:in son

我们再用继承的知识再来做下面一个题目:

class Animal:
    def __init__(self,name,food):
        self.name = name
        self.food = food
        self.blood = 100
        self.wise = 100

    def eat(self):
        print(f'{self.name} is eating {self.food}!')

    def drink(self):
        print(f'{self.name} is drinking')

    def sleep(self):
        print(f'{self.name} is sleeping!')

class Cat(Animal):
    def __init__(self, name, food, eye_color):
        Animal.__init__(self, name, food) # 调用了父类的初始化,去完成一些通用属性的初始化
        self.eye_color = eye_color # 派生属性,在自己的初始化函数来书写

cat = Cat('小白','猫粮','蓝色')
print(cat.__dict__)
# {'name': '小白', 'food': '猫粮', 'blood': 100, 'wise': 100, 'eye_color': '蓝色'}

在这里我们总结一下继承的所有知识:

1.继承语法:class 子类名(父类名):pass class 子类名(父类名1,父类名2...):pass

2.子类可以使用父类中的:方法和静态变量(静态属性)

3.当子类和父类的方法重名的时候,我们只使用子类的方法,而不是去调用父类的方法了。(在java语言中这个叫做方法的重写)

4.子类可以使用父类中的方法,但是也可以重写父类中的方法,同时也可以使用静态变量。

5.父类和子类的方法的选择

子类永远优先调用自己的方法,自己没有就用父类的方法。如果自己有,还想用父类的,直接在子类方法中调用父类的方法。调用的方法:父类名.方法名(self)、父类名.方法名(对象)

6.父类和子类的属性的选择

子类永远优先调用自己的属性,自己没有就用父类的属性。如果自己有,还想用父类的,直接在子类中调用父类的属性。调用的方法:父类名.属性

7.其实子类和父类的属性和方法等等之间的关系实质上是面向对象的命名空间等之间的关系,只要掌握了面向对象命名空间之间的关系,子类与父类的属性和方法等等关系我们也可以弄的很清楚。

2)单继承和多继承

我们现在来学习一下单继承和多继承的知识。

单继承:只要继承了一个父类就叫做单继承(一个父类)

class D:
    pass
class C(D):
    pass
class B(C):
    pass
class A(B):
    pass

多继承:继承了两个或者两个以上的父类就叫做多继承(多个父类)

有一些语言是不支持多继承的,例如java语言。python和C++语言的特点是都可以在面向对象中支持多继承。

class A:
    def func(self):
        print('in A')
class B:
    def func(self):
        print('in B')
# 只是关系谁写在前面谁写在后面
class C(A,B):pass

c = C()
c.func() # in A

总结:

单继承---调子类的:子类自己有的时候。调父类的:子类自己没有的时候。调子类和父类的:子类和父类都有,在子类中调用父类的。

多继承---一个类有多个父类,在调用父类的时候,按照继承的顺序,先继承的先寻找。

3)类的继承顺序(单继承和多继承)

我们讲解类的继承的顺序的时候,我们先讲解一下新式类和经典类。

首先我们要明白只要继承object类就是新式类,不继承object类的都是经典类。python3中所有的类都是继承object类,所以都是新式类,所以python3中所有类都是新式类。

经典类:在python3中不存在,在python2中不继承object的类都是经典类,继承object类的就是新式类。所以经典类在python3中不存在,在python2中不主动继承object的类。

# 以下两个是新式类,因为在python3中
# class A:pass
# class A(object):pass

# 在python2中
# class A:pass  # 经典类
# class A(object):pass  # 新式类

在单继承方面(无论是新式类还是经典类都是一样的,都是往深度找。遵循深度优先遍历的算法)

class A:
    def func(self):
        pass

class B(A):
    def func(self):
        pass

class C(B):
    def func(self):
        pass

class D(C):
    def func(self):
        pass

d = D()
d.func()

上面代码寻找某一个方法的顺序(func函数):D-B-C-A-object。越往父类走,是深度。

在多继承方面(经典类的多继承的寻找顺序是符合深度优先遍历。新式类是广度优先遍历,同时也遵循C3算法)

# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     def func(self):
#         print('b')
#
# class C(A):
#     def func(self):
#         print('c')
#
#
# class D(B,C):
#     def func(self):
#         print('d')
#
# d = D()
# d.func()  # d

# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     def func(self):
#         print('b')
#
# class C(A):
#     def func(self):
#         print('c')
#
#
# class D(B,C):
#     pass
#
# d = D()
# d.func()  # b


# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     pass
#
# class C(A):
#     def func(self):
#         print('c')
#
#
# class D(B,C):
#     pass
#
# d = D()
# d.func()  # c

#
# class A:
#     def func(self):
#         print('a')
#
#
# class B(A):
#     pass
#
# class C(A):
#     pass
#
#
# class D(B,C):
#     pass
#
# d = D()
# d.func()  # a

在走到一个点,下一个点既可以从深度走,也可以从广度走的时候,总是先走广度,再走深度,这一个过程就叫做广度优先遍历。

经典类---经典类的多继承的寻找顺序是符合深度优先遍历。在走到一个点,下一个点既可以从深度走,也可以从广度走的时候,总是先走深度,再走广度,这一个过程就叫做深度优先遍历。深度优先类,在找方法的时候,总是先往深了找,一直找到头,没有再找下一条线。在经典类中,都是深度优先,总是在一条路走不通之后再换一条路,走过的点不会再走了。

新式类---新式类的多继承的寻找顺序是符合广度优先遍历同时也遵循C3算法。

class A:
    def func(self):
        print('a')

class B(A):
    def func(self):
        print('b')
class C(A):
    def func(self):
        print('c')
class D(B,C):
    def func(self):
        print('d')

d = D()
d.func()
# D->B->C->A
# [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
print(D.mro())

以下是我们掌握新式类算法的推理过程图:

| |
| |
| |
| |
| |
| |

经典类---深度优先 新式类---广度优先

深度优先遍历算法,先要会自己看,自己能搞出顺序来。

广度优先遍历算法遵循C3算法,要会用mro,会查看顺序。经典类没有mro,但是新式类有。

4)多态

我们知道python一切皆对象,同时python处处是多态。

# 多态是站在java的角度来讲述
# python中的参数是没有类型的约束的,在执行的过程当中python解释器会自动的识别这个是什么类型的,但是java的参数必须指明数据类型。
# def add(a,b):
#     return a+b

# print(add(1,2))
# print(add(1,'adaduqwhdu')) # TypeError: unsupported operand type(s) for +: 'int' and 'str'

# class Payment:pass
#
# # 微信支付的类
# class WeChat(Payment):
#     def __init__(self, name):
#         self.name = name
#
#     def pay(self, money):
#         dic = {'username': self.name, 'money': money}
#         # 想办法调用微信支付的url连接,把dic传递过去
#         print('%s通过微信支付%s钱成功' % (self.name, money))
#
#
# # 苹果支付类
# class Apple(Payment):
#     def __init__(self, name):
#         self.name = name
#
#     def pay(self, money):
#         dic = {'name': self.name, 'number': money}
#         # 想办法调用微信支付的url连接,把dic传递过去
#         print('%s通过微信支付%s钱成功' % (self.name, money))

# java实现方式
# def pay(Payment obj,int money):
#     obj.pay(money)
#
# obj = Apple('alex')
# pay(obj,400)
#
# obj = WeChat('alex')
# pay(obj,400)

总结:一种类型表现出来的多种状态

支付表现出的微信支付和苹果支付的两种状态

在java情况下:一个参数必须指明指定类型

所以如果想让两个类型的对象都可以传,那么必须让这两个类继承自一个父类,在指定类型的时候使用我们的父类来指定类型

python处处是多态

5)封装

封装:就是把属性和方法装起来就是封装。

封装有两种:

一种是广义上的封装:大家都基本认可的封装。把属性和方法装起来,不能直接调用了。只能通过类和对象调用。

一种是狭义上的封装:一些细节上的封装。把属性和方法藏起来,外面不能调用,只能在内部偷偷调用。

class A:
    a = 1
    b = 2
    def fun(self):
        print('in fun')
print(a)

上面是打印不出来a的值的。这样已经是不可以调了,某种意义上是已经封装了。需要通过类的名字去调用。这个从某种意义上说已经封装了。我们之前写的很多的例子实际上都是广义上的封装。

现在我们学习一下狭义上的封装。

狭义上的封装可以分为三种。

第一种是实例变量(实例属性)的封装(私有化)。给一个名字前面加上了双下划线的时候,这个名字就变成了一个私有的。所有的私有的内容或者名字都不能在类的外部调用,只能在类的内部使用了。但是我们可以设置方法,使得外部可以获取到私有的实例变量,和改变私有的实例变量的值。

1.不能看不能改

class User:
    def __int__(self, name, passwd):
        self.user = name
        self.__pwd = passwd

zhouqian = User('alex','sbsbsb')
print(zhouqian.pwd)#报错

2.只能看,但是不能改

class User:
    def __int__(self, name, passwd):
        self.user = name
        self.__pwd = passwd
    def get_pwd(self): # 表示的是用户不能改只能看  私有+某个get方法实现的
        return self.__pwd  # 通化这种方式外部可以获取到这个值,类的外部调用,获取到这个值。
zhouqian = User('zhouqian','sbsbsb')

3.可以看,可以改

class User:
    def __int__(self, name, passwd):
        self.user = name
        self.__pwd = passwd  # 我们这里的pwd就是一个私有的实例属性(实例变量)私有的实例变量,私有的对象属性
    def get_pwd(self): # 表示的是用户不能改只能看  私有+某个get方法实现的
        return self.__pwd  # 通化这种方式外部可以获取到这个值,类的外部调用,获取到这个值。
    def change_pwd(self,pwd): # 表示用户必须调用我们自定义的修改方式来进行变量的修改 私有+change方法实现
        self.__pwd = pwd
# 给一个名字前面加上了双下滑先的时候,这个名字就变成了一个私有的

alex = User('alex','sbsbsb')
print(alex.pwd) #在前面加上__,就会影藏掉,就调用不到
print(alex.__pwd) #在前面加上__,就会影藏掉,就调用不到

第二种是静态变量(静态属性)的封装(私有化)。给一个名字前面加上了双下划线的时候,这个名字就变成了一个私有的。所有的私有的内容或者名字都不能在类的外部调用,只能在类的内部使用了。但是我们可以设置方法,使得外部可以获取到私有的实例变量,和改变私有的实例变量的值。同时也有三种形式:1.不能看不能改,2.只能看,但是不能改,3.可以看,可以改。

class User:
    __country = 'China' # 私有的静态变量(静态属性)
    def func(self):
        print(self.__country) # 在内的内部可以使用,在内的外部不能被调用
        print(User.__country) # 在内的内部可以使用,在内的外部不能被调用
    def get_contry(self):
        return User.__country
    def change_country(self):
        User.__country = 'hangzhou'
# print(User.country) # AttributeError: type object 'User' has no attribute 'country'
# print(User.__country) # AttributeError: type object 'User' has no attribute 'country'

user = User()
user.func()
User.func(user)
User().func()
print(User().get_contry())
user1 = User()
user1.change_country()
print(user1.get_contry())
'''
输出的结果为:
China
China
China
China
China
China
China
hangzhou
'''

第三种是方法的封装(私有化)

import hashlib
class User:
    def __init__(self,name,passwd):
        self.user = name
        self.__pwd = passwd # 私有的实例变量
    def __get_md5(self): # 私有的方法
        md5 = hashlib.md5(self.user.encode('utf-8'))
        md5.update(self.__pwd.encode('utf-8'))
        return md5.hexdigest()
    def getpwd(self):
        return self.__get_md5()

user = User('alex','123456')
# user.get_md5() # 这个方法变成私有的了,所以这里是调用不到的。外部值调用不到的,只能在内部调用
# user.__get_md5() # AttributeError: 'User' object has no attribute 'get_md5'
pwd = user.getpwd()
print(pwd) # 94e4ccf5e2749b0bfe0428603738c0f9

总结:所有的私有化都是为了让用户不在外部调用类中的某个名字(属性或者方法)。如果完成私有化,那么这个类的封装度就更高了,封装度越高,类中的各种属性和方法的安全性越高,但是实现的过程代码越复杂。

在这里我们要明白一个事情,就是加了双下划线的名字(方法或者属性)为啥不能从类的外部调用了?

class User:
    __country = 'china'
    def func(self):
        print(self.__country)# 在类的内部使用的时候,自动的把当前这句话所在的类的名字拼在私有变量前完成变形
print(User.__dict__)
print(User._User__country) # china
{'__module__': '__main__',  'func': <function User.__func at 0x000001C9E16AC708>, 'country': 'china', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}
{'__module__': '__main__', '_User__func': <function User.__func at 0x000001C9E16AC708>'_User__country': 'china', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}

User.__aaa = 'bbb' # 在类的外部根本不能定义类的私有的概念

我们还要明白一个问题是,私有的内容能不能被子类使用呢?答案是不能。私有的内容不能被子类使用。下面两个程序,我们需要注意,同时也有相应的分析程序的过程。

class Foo(object):
    def __init__(self):
        self.func()

    def func(self):
        print('in Foo')
class Son(Foo):
    def func(self):
        print('in Son')
Son() # in son


class Foo(object):
    def __init__(self):
        self.__func()

    def __func(self):
        print('in Foo')
class Son(Foo):
    def __func(self):
        print('in Son')
Son() # in Foo

| |

在其他语言中的数据的级别都有哪些?在python中有哪些呢?

public 共有的 类内和类外都可以用,父类和子类都可以用 python支持 java c++支持

protect 保护的 类内可以用,父类子类都可以用,类外不能用 python不支持 java c++支持

private 私有的 在本类的类的内部可以用,类内可以用,本类能用,父类子类不能用,类外不能用 python支持 java c++支持

posted @ 2020-07-13 11:41  AndreasZhou  阅读(365)  评论(0编辑  收藏  举报