1、封装
- 【面试题】面向对象的三大特征:
- 封装、继承和多态
- 1.1、封装
- 广义封装:函数和类
- 狭义封装:在面向对象中,一个类中的某些属性,如果不希望被外界直接访问,则可以将该属性私有化,该属性只能在当前类中被直接访问,如果在类的外面需要访问【获取或修改】,则可以通过暴露给外界的函数间接访问。
- 封装本质:将类中的属性进行私有化。
#==================================================
#1、私有属性
class People():
def __init__(self,name,age):
#不是私有属性的,叫做公开属性
self.name = name
#私有属性:只要在属性名前面加上两个下划线
self.__age = age #注意__age就是一个私有属性
def show(self):
print(self.name)
print(self.__age)
#间接访问私有属性函数
def get_age(self):
return self.__age
#间接修改私有属性函数
def set_age(self,age):
self.__age = age
#1.1、对比公开属性和私有属性的区别
zh = People('张总',14)
print(zh.name) #张总
# print(zh.__age) #AttributeError: 'People' object has no attribute '__age'
zh.show() #14
#1.2、属性私有化原理
# print(zh._People__age) #14
#1.3、通过类的内部函数对私有属性进行间接访问和修改
print('*' * 30)
a = zh.get_age() #(间接访问)
print(a) #14
zh.set_age(20) #(间接修改)
a = zh.get_age() #(间接访问)
print(a) #20
#1.4、修饰器:@property 和 @xxx.setter (property n. 所有物、性质)
class A():
def __init__(self,a):
self.__a = a
@property
def a(self): #相当于Sa.get_a()
return self.__a
@a.setter
def a(self,a): #相当于Sa.set_a()
self.__a = a
sa = A(11)
print(sa.a) #会调用被@property装饰的函数,相当于调用Sa.get_a()
sa.a = 15 #会调用被@xxx.setter装饰的函数,相当于调用Sa.set_a()
print(sa.a) #
'''
总结:
a.一个属性一旦被私有化,只能在类的内部被访问,在类的外面无法直接访问
b.属性被私有化之后,私有属性在内存中可能以另外一种形式存在,常见形式:_类名__属性名
但是,不建议使用,因为在不同的系统中,不同的版本下,存在形式可能不同,违反了Python跨平台的特点。
c.
私有属性可以通过类函数间接访问和修改
d.@property:修饰某个函数,该函数会被当做属性使用,该函数的函数名相当于是一个变量名,指向该函数的返回值
作用:在私有属性的使用中,为了获取私有属性的值
@xxx.setter:修饰某个函数,该函数会被当做属性使用,xxx必须和被@property装饰的函数名相同
作用:在私有属性的使用中,为了给私有属性修改值
注意:为了提高代码的可读性,同一个私有属性暴露给外界的函数名建议同名,使用私有属性的属性名
(也就是说,同一个私有属性的访问和修改函数建议同名)
'''
#==================================================
#2、不同形式的属性
'''
【面试题】解释下面不同形式的变量出现在类中的意义
a:普通属性,也被称为公开属性,在类的外面可以直接访问 ****
_a:在类的外面可以直接访问,但是不建议使用,容易和私有属性混淆
__a:私有属性,只能在类的内部被直接访问。类的外面可以通过暴露给外界的函数访问 *****
__a__:在类的外面可以直接访问,但是不建议使用,因为系统属性和魔术方法都是这种形式的命名,
如:__slots__ __init__ __new__ __del__,__name__,__add__,__sub__,__mul__等
'''
class Person():
def __init__(self,name,age,weight,score):
self.name = name
self._age = age
self.__weight = weight
self.__score__ = score
p = Person("小明",10,150,80)
print(p.name)
print(p._age)
# print(p.__weight) #AttributeError: 'Person' object has no attribute '__weight'
print(p.__score__)
#==================================================
#3、私有函数
class Person():
# 公开函数
def func1(self):
print("func~~~222")
self.__show()
# 私有函数
def __show(self):
print("showing")
p = Person()
p.func1() #func~~~222 showing
# p.__show() #AttributeError: 'Person' object has no attribute '__show'
"""
注意:
a.如果一个函数需要被私有化,则可以在类中定义一个公开函数,在公开函数中调用私有函数即可
b.类中的实例函数之间相互调用,语法:self.函数名(形参)
c.类的外部无法访问私有函数
"""
2、继承
-
继承:如果两个或者两个以上的类具有相同的属性和方法,我们可以抽取一个类出来,在抽取出来的类中声明各个类公共的部分
-
被抽取出来的类——父类【father class】 超类【super class】 基类【base class】
-
两个或两个以上的类——子类 派生类
-
他们之间的关系——子类 继承自 父类 或者 父类 派生了 子类
-
-
简单来说,一个子类只有一个父类,被称为单继承
-
注意:object是Python中所有类的根类,如果一个类没有明确的父类,默认继承自object。
-
继承的特点:继承可以简化代码、提高代码复用性、提高代码的可扩展性。(可以参考如下代码来理解继承的特点)
# 父类
class Person():
def __init__(self,name,age):
self.name = name
self.age = age
def eat(self):
print("eating")
# 子类
class Student(Person):
def __init__(self,name,age,type):
self.type = type
def study(self):
print("studying")
class Doctor(Person):
def __init__(self,name,age,kind):
self.kind = kind
def assist(self):
print("assisting")
class Worker(Person):
pass
- 单继承:只继承一个父类
# 二、继承
#2.1、单继承
class Father(object):
def __init__(self,name,age):
print('父类init')
self.name = name
self.age = age
def xing(self):
print('姓:张')
#测试子类当中无构造函数init
class Son(Father):
pass
zhang_1 = Son('张一',15) #父类init
print(zhang_1.name,zhang_1.age) #张一 15
zhang_1.xing() #姓:张
print('1*' * 10)
#测试子类当中有构造函数init,对父类属性不赋值
class Daughter(Father):
def __init__(self,name,age,hobby): #这里init会警告
self.hobby = hobby
zhang_2 = Daughter('张二',15,'踢球') #父类init
print(zhang_2.hobby) #踢球
# print(zhang_2.name,zhang_2.age) #AttributeError: 'Daughter' object has no attribute 'name'
zhang_2.xing() #姓:张 (子类对象可以访问父类中未被私有化的属性和调用父类中未被私有化的函数)
print('2*' * 10)
#测试子类当中有构造函数init,对父类属性赋值
class Daughter(Father):
def __init__(self,name,age,hobby): ##这里init会警告
self.name = name #如果在子类中定义了构造函数,则创建子类对象,调用自身的构造函数
self.age = age
self.hobby = hobby
zhang_2 = Daughter('张二',15,'踢球') #父类init
print(zhang_2.hobby) #踢球
print(zhang_2.name,zhang_2.age) #张二 15
zhang_2.xing() #姓:张
print('3*' * 10)
#如何继承父类属性
'''
a.super(Doctor, self).__init__(name, age)【self表示当前对象,super表示跟父类有关,不常用】
b.super().__init__(name,age)【可以理解为a的简写,常用于单继承中】
c.父类名.__init__(self,name,age)【不要忘记加self,因为父类名不是对象不会自动加。常用于多继承】
'''
#测试子类当中有构造函数init,且要求继承父类属性(理由是设计继承是为了简化代码!每次子类中构造函数不用再对父类中有的属性进行赋值)
class LikeFather(Father):
def __init__(self,name,age,hobby):
#也可以用super().__init__(name,age)
Father.__init__(self,name,age) #在子类的构造函数中调用父类的构造函数,目的是为了给当前对象动态绑定公共的属性
self.hobby = hobby
father = Father('张爸',50)
print(father.name,father.age) #张爸 50
zhang_3 = LikeFather('张三',18,'睡觉') #父类init
print(zhang_3.name,zhang_3.age,zhang_3.hobby) #张三 18 睡觉
zhang_2.xing() #姓:张
print(father.name,father.age) #张爸 50
'''
总结:
a.对象初始化一次就要调用一次对应的类的构造函数
b.如果子类中未定义构造函数,子类会继承父类中的构造函数,所以创建子类对象,会自动调用父类中的构造函数
c.如果子类中定义了构造函数,如果子类对象需要访问父类中的属性,则在子类的构造函数中调用父类的构造函数
方法参考“如何继承父类属性(父类属性也叫公共属性)”
'''
#==================================================
print('--2.2、继承中的slots--')
#2.2、继承中的slots(限制类可以绑定的属性)
class Animal():
__slots__ = ("name","age",'__num') #私有属性也可以在slots中
def __init__(self,name,age,num):
self.name = name
self.age = age
self.__num = num
def show(self):
print("showing")
def __func(self):
print('func~~~~~~')
a = Animal('小白',10,6)
print(a.name,a.age) #小白 10
# print(a.__num) # AttributeError: 'Animal' object has no attribute '__num'
a.show() #showing
# a.__func() # AttributeError: 'Animal' object has no attribute '__func'
class Cat(Animal):
# __slots__ = ("name", "age", '__num')
pass
c = Cat('aaa',34,8)
print(c.name,c.age) #aaa 34
# print(c.__num)
c.show() #showing
c.hobby = 'dance' # 这里说明父类的slots限制没有传给子类
print(c.hobby) #dance
# 结论:父类中定义的__slots__,无法继承给子类,子类如果需要限制和父类相同的属性,则必须再次定义__slots__
#这也说明了,继承中不是父类的所有东西都会传给子类
#==================================================
#2.3、继承中的类属性
class Animal(object):
num = 10
class Dog(Animal):
pass
# 如果子类中未定义类属性,则子类继承自父类,当父类中的类属性更改,子类的类属性随着更改
print(Animal.num,Dog.num) # 10 10
Animal.num = 66
print(Animal.num,Dog.num) # 66 66
Dog.num = 100 #这里相当于给子类创建了一个新的属性num,和父类中的num不是一回事
print(Animal.num,Dog.num) # 66 100
Animal.num = 888
print(Animal.num,Dog.num) # 888 100
# 【面试题】
class MyClass(object):
x = 10
class SubClass1(MyClass):
pass
class SubClass2(MyClass):
pass
print(MyClass.x,SubClass1.x,SubClass2.x) # 10 10 10
SubClass1.x = 20
print(MyClass.x,SubClass1.x,SubClass2.x) # 10 20 10
MyClass.x = 30
print(MyClass.x,SubClass1.x,SubClass2.x) # 30 20 30
- 多继承:顾名思义,多继承就是一个子类可以有多个父类,比如:一个孩子有一个父亲,一个母亲
#2.4、多继承
# 父类
class SuperClass1():
def __init__(self,num):
self.num = num
def func1(self):
print("func~~~~1111")
class SuperClass2():
def __init__(self,a,b):
self.a = a
self.b = b
def func2(self):
print("func~~~~22222")
# 子类
# 1.在子类中未定义构造函数
"""
注意:
如果一个子类继承自多个父类,当子类中未定义构造函数
默认情况下,创建子类对象的时候,调用的是父类列表中第一个父类中的构造函数
"""
class SubClass1(SuperClass2,SuperClass1):
pass
sub1 = SubClass1(34,10)
print(sub1.a,sub1.b) #34 10
# print(sub1.num) # AttributeError: 'SubClass1' object has no attribute 'num'
sub1.func1() #func~~~~1111
sub1.func2() #func~~~~22222
print('1*' * 20)
# 2.在子类中定义构造函数
"""
注意:
如果一个类继承自多个父类,在子类中定义了构造函数
而且子类需要继承父类中所有的属性,则只需要在子类的构造函数中调用父类中的构造函数
"""
class SubClass2(SuperClass2,SuperClass1):
def __init__(self,num,a,b,num1):
# 调用父类的构造函数
# 说明:在多继承中,如果需要调用多个父类的构造函数,则无法使用super().__init__(),因为默认情况下调用的是父类列表中第一个父类中的构造函数,则建议使用 父类类名.__init__(self,属性...)
SuperClass1.__init__(self,num)
SuperClass2.__init__(self,a,b)
self.num1 = num1
sub2 = SubClass2(10,20,30,40)
print(sub2.num1) #40
print(sub2.a) #20
print(sub2.num,sub2.b) #10 30
sub2.func1() #func~~~~1111
sub2.func2() #func~~~~22222
'''
总结:
a.继承的目的是为了简化代码,所以一般不会继承一个空类
b.继承要合理,不能因为父类有子类可以用的函数就随便给子类继承
'''