目录
封装:
特点:
1.可理解为私有化属性
2.在类中定义公有set方法和get方法,通过调用这两个方法来操作私有化属性
3.被私有化的属性不能被继承
在Python中,封装的概念可理解为私有化。那么我们为什么要提出私有化呢,其实将属性私有化可以提升它的安全级别,访问仅限于类中,不能随意被外界修改。下面简单介绍一下私有化的概念。
私有化:
它的好处:
1.隐藏属性不能随意被外界修改
2.当需要修改私有化时,可通过函数进行修改
def setXXX(self,xxx): #通过set对私有化属性进行重新赋值
if xxx符合条件:
self.__xxx = xxx
else:
不赋值
3.当需要获取私有化时,也通过函数获取
def getXXX(self):
return self.__xxx
Python中的dir()函数:
dir()函数不带参数时,以列表形式返回当前范围内的变量,方法和定义的类型;
带参数时,以列表形式返回参数的属性,方法。
同 __dir__() 的用法相同
举例说明:
class Student:
__age = 18 # 私有化的类属性
def __init__(self, name, age):
self.__name = name
self.__age = age
self.__score = 60 # 外部无法访问,无法修改
# 定义公有set和get方法
# set是为了赋值
def setAge(self, age): # 可以改变私有化属性的值
if age > 0 and age <= 100:
self.__age = age
else:
print('年龄不符合规定值!')
# get是为了取值
def getAge(self):
return self.__age
def __str__(self):
return '姓名:{},年龄:{},分数:{}'.format(self.__name, self.__age, self.__score)
one = Student('JSY', 20)
print(one)
one.age = 21
one.name = 99
print(one)
one.setAge(28)
print(one)
# dir(对象名/类名) 或者 (对象名/类名).__dir__()
print(dir(one))
print(dir(Student))
print(one._Student__age) # 其实它就是__age,只是系统自动改名了,由此可见,私有化也是伪私有的
print(Student._Student__age) # 通过该方法访问到了类的私有化属性
print(one.__dir__())
print(__name__) # 表示当前程序运行在哪一个模块中
输出:
姓名:JSY,年龄:20,分数:60
姓名:JSY,年龄:20,分数:60
姓名:JSY,年龄:28,分数:60
['_Student__age', '_Student__name', '_Student__score', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'getAge', 'name', 'setAge']
['_Student__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'getAge', 'setAge']
28
18
['_Student__name', '_Student__age', '_Student__score', 'age', 'name', '__module__', '__init__', 'setAge', 'getAge', '__str__', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
__main__
解读:第一行输出为创建对象,而分数的属性已经被固定,无需传入参数;
第二行输出,是想要直接修改类中的私有化属性,修改失败,故属性无变化
第三行输出,采用(set和get)函数的方式修改私有化属性,修改成功
接下两行输出,为对象和类中的属性及方法输出
接下两行输出,为调用系统改名后的私有化属性,故私有化本身是伪私有的
倒数第二行输出,为使用__dir__()的方法获取对象的属性及方法
最后一行输出,__name__方法可返回当前程序在哪一个模块中运行
@property装饰器:
开发中会使用这种方式进行私有化的处理,代替set和get,来获取和修改属性。
格式:
@property
def func1(self): #先定义获取私有属性
return self.__xxx #返回私有属性值
@func1.setter
def func2(self,xxx): #再定义修改私有属性
self.__xxx=xxx
###代码示例###
class Student:
__age = 18 # 私有化的类属性
def __init__(self, name, age):
self.__name = name
self.__age = age
self.__score = 60 # 外部无法访问,无法修改
#先有getxxx
@property
def acquire_age(self):
return self.__age
#再有setxxx,因为get依赖set
@acquire_age.setter
def assignment_age(self, age):
if age > 0 and age < 100:
self.__age = age
else:
print('年龄不符合规定值 !')
def __str__(self):
return '姓名:{},年龄:{},分数:{}'.format(self.__name, self.__age, self.__score)
s = Student('Tom', 99)
s.assignment_age = 98 #给__age赋值
print(s.acquire_age) #获取__age的值
输出:
98
此时,可直接通过函数名assignment_age来给私有属性__age赋一个新的值
再通过函数acquire_age,返回私有属性__age的值
继承:
在讲继承之前我们先看一个例子:
import random
# 声明(Road)
class Road:
def __init__(self, name, len):
self.name = name
self.len = len
# 声明(Car)
class Car:
def __init__(self, brand, speed):
self.brand = brand
self.speed = speed
def get_time(self, road): # 需注意:road与r指向同一地址空间
ran_time = random.randint(1, 10)
message = '{}品牌的车在{}上以{}速度行驶{}小时'.format(self.brand, road.name, self.speed, ran_time)
print(message)
def __str__(self):
return '{}品牌,速度:{}'.format(self.brand, self.speed)
# 创建实例化对象
r = Road('魏州大道', 6666)
c = Car('凯迪拉克', 66)
print(c)
c.get_time(r)
输出:
凯迪拉克品牌,速度:66
凯迪拉克品牌的车在魏州大道上以66速度行驶6小时
代码注意:在Car类里的get_time方法,其参数可以是一个对象类型,且该对象可以是其他类的, 故在调用该方法的时候,可以传入Road的对象,借此来调用Road里的属性或是方法。
该例其实是一个 has a 的包含关系
在这个例子中我们看到了类与类直接的联系,而继承则是面向对象中的重要的类之间的关系。python中的继承可分为两种 has a 和 is a,下面介绍这两种情况。
has a:
has a 强调一种包含的关系,比如:如果A中有B,那么,B就是A的组成部分;当一个类中使用了另外一种自定义的类型(类或对象等),此时我们认为出现了has a 的包含现象。
###举例###
class Computer:
def __init__(self, brand, type, color):
self.brand = brand
self.type = type
self.color = color
def online(self):
print('正在玩LOL中!')
def __str__(self):
return self.brand + '---' + self.type + '---' + self.color
class Book:
def __init__(self, book_name, author, number):
self.book_name = book_name
self.author = author
self.number = number
def __str__(self):
return self.book_name + '---' + self.author + '---' + str(self.number)
class Student:
def __init__(self, name, computer, book):
self.name = name
self.computer = computer
self.books = []
self.books.append(book)
def borrow_book(self, input_book):
# print('书籍信息:')
# print(book) #相当与调用对象名book1
for book in self.books:
if book.book_name == input_book.book_name:
print('已经借过此书!')
break
else:
self.books.append(input_book)
print('借书成功!')
break
print(self.books)
def show_book(self):
for book in self.books:
print(book.book_name)
def __str__(self):
return self.name + '---' + str(self.computer) + '---' + str(self.books) # 不能返回一个对象
# 创建对象
computer = Computer('hp', 'pro++', 'red') # computer 对象类型
book = Book('武动乾坤', '天蚕土豆', 15)
stu = Student('JJ', computer, book) # 此时出现了 包含关系 (stu对象包含了computer对象和book对象)
print(stu)
book1 = Book('斗破苍穹', '天蚕土豆', 20)
stu.show_book()
stu.borrow_book(book1)
输出:
JJ---hp---pro++---red---[<__main__.Book object at 0x0000021AA3F7C610>]
武动乾坤
借书成功!
[<__main__.Book object at 0x0000021AA3F7C610>, <__main__.Book object at 0x0000021AA3F7C520>]
武动乾坤
斗破苍穹
从代码:stu = Student('JJ', computer, book) 可以看出,定义一个Student对象的时候,传入了 Computer的对象和Book的对象,故在Student类中可以对它们的(非私有化)属性进行调 用。
is a:
is a 可以说是真正意义上的继承关系:如果A是B的一种,那么B就是A的基类(base class / 父类);比如:等边三角形是三角形,则三角形是等边三角形的基类。
特点:
1.私有化属性不能被继承
2.如果子类中没有定义__init__,自动调用父类的__init__方法
3.如果子类继承父类需要定义自己的__init__方法(来初始化新的属性等操作),需要在__init__里先 调用一下父类的__init__,再去初始化新的属性或其他操作。
4.调用父类的方法:super关键词
5.如果父类有eat(),子类也定义一个eat()方法,按照就近原则:先找当前类,再去找父类
如果子类出现父类同名的方法,可称之为 override--重写(覆盖)。
此情况一般出现在:父类提供的方法不能满足子类的需求
6.子类中可调用父类的同名方法:
使用super().方法名(参数) #可调用父类的方法,再写新增的方法(内容)
对于super关键词的用法介绍:
Python中的super()方法设计目的是用来解决多重继承时父类的查找问题,所以在单重继承中用不用super都没关系;但是,使用super()是一个好的习惯。一般我们在子类中需要调用父类的方法时才会这么用。
1.super(cls,obj) #即传入类名+对象名
obj对象必须是cls类的对象(cls的子类的对象当然也是cls类的对象),记作 type(obj) <= cls;具有判断功能
2.super(cls1,cls2) #即传入两个类名
cls2必须是cls1的子类或是本身,记作cls2<=cls1 (右的范围小于左);具有判断功能
3.super().__init() #等同于 super(A,self).__init__()
使用格式:
class Student(Person): #可理解为所有学生都是人,Person是父类
pass
###举例说明###
# 可以定义一个“总类”
class Person:
def __init__(self, name,age):
self.name = name
self.age = age
def eat(self):
print(self.name + '正在吃饭!','年龄:'+str(self.age))
# 在定义这种相似的类时,可能会出现许多重复的属性或方法,此时需要使用到*继承*
class Student(Person): # 此为继承的格式
def __init__(self, name,age):
print('--->Student的init')
# 如果要定义自己的__init__,还需调用父类的__init__;可使用super来调用父类的方法
# super(Student, self).__init__() # super另一种使用方式
super().__init__(name,age) # super() == 父类Person ;调用父类的__init__方法
super(Student, self).__init__()
class Employee(Person):
def __init__(self,name,age,work): #新增的属性work,需要在本类中进行处理
print('--->Employee的init')
super().__init__(name,age) #调用父类的__init__先将self.name 和 self.age 获取
self.work = work #独有属性在本类的__init__里进行初始化
def __str__(self):
return '姓名:'+self.name+'年龄:'+str(self.age)+'职业:'+self.work
def eat(self):
super(Employee, self).eat() #在子类中调用父类的同名方法,再向下写新增的内容
print(self.name+'正在吃饭! ')
class Doctor(Person):
def __init__(self,name,age,department):
super(Doctor, self).__init__(name,age) #super(类名,对象).__init__();底层进行判断:判断该对象是否是以这个类构建出来的
self.department = department
def __str__(self):
return '姓名:'+self.name+'年龄:'+str(self.age)+'部门:'+self.department
s = Student('Jack',16)
s.eat()
print(s.name)
e = Employee('Tom',29,'程序员')
print(e)
e.eat() #此时由于Employee本类中就有eat方法,无需去调用父类(Person)里的eat方法,故此为就近原则
d = Doctor('华佗',88,'老中医')
print(d)
d.eat()
输出:
--->Student的init
Jack正在吃饭! 年龄:16
Jack
--->Employee的init
姓名:Tom年龄:29职业:程序员
Tom正在吃饭! 年龄:29
Tom正在吃饭!
姓名:华佗年龄:88部门:老中医
华佗正在吃饭! 年龄:88
本例中,Person为父类,Student,Employee,Doctor,均继承它,而这些子类都有一些本身才具有的性质,故需要重写父类的__init__方法;此外,继承中的检索按照就近原则,先检索本类,再到父类。
多继承(了解):
Python允许多重继承:一个子类可以有多个父类(java不可以) #开发中很少使用
class 子类(父类1,父类2...):
pass
多重继承的搜索顺序:在Python2中,对于经典类和新式类有所不同
经典类: #Python2中是从左至右,深度优先
class Base:
pass
class A(Base):
pass
class B(Base):
pass
新式类:
class Base(object): #区别在此,python2中是广度优先
pass
class A(Base):
pass
class B(Base):
pass
注意:在python3里,无论经典类还是新式类都是广度优先
查看搜索顺序的方法:1.类名.__mro__ #直接使用查看
2.import inspect #导包后,使用getmro()方法查看
print(inspect.getmro(类名))
###举例说明###
#C继承了A和B,而A和B继承了Base
#Base-->A,B-->C
class Base:
def test(self):
print('--->Base')
class A(Base):
def test1(self):
print('--->A')
class B(Base):
def test2(self):
print('--->B')
class C(A,B): #同时继承多个父类
def test(self):
print('--->C')
import inspect
print(inspect.getmro(C)) #显示的可理解为搜索顺序
print(C.__mro__) #同上
c = C()
c.test1() #可以使用多个父类提供的方法
c.test2()
# c.test3()
c.test() #出现与父类同名方法时,搜索顺序为就近原则
输出:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
--->A
--->B
--->C
注意:前两行输出为继承类的搜索顺序
最后的输出是 调用的C类中的test方法,由于出现与父类同名的方法,搜索顺序为就近原则
多态:
实际上,python并无严格多态概念。多态可理解为:一种事物的多个形态,对于继承可以认为是为了精简代码,而多态是对精简后代码的灵活使用,因而多态依赖于继承。
##示例##
class Person:
def __init__(self,name):
self.name = name
def feed_pet(self,pet): #此时出现了多态;参数pet既可以接收cat,也可以接收dog,还可以接收tiger(不是Pet的子类)或其他对象
if isinstance(pet,Pet): #判断了pet是否是Pet或Pet子类的对象
print('{}喜欢养宠物:{},昵称是:{}'.format(self.name,pet.role,pet.nickname))
else:
print('这个不是宠物!可能有危险!!!')
class Pet:
role = 'Pet'
def __init__(self,nickanme,age):
self.nickname = nickanme
self.age = age
def show(self):
print('昵称:{},年龄:{}'.format(self.nickname,self.age))
class Cat(Pet):
role = '猫'
def catch_mouse(self):
print('抓老鼠...')
class Dog(Pet):
role = '狗'
def watch_house(self):
print('看家...')
class Tiger:
def eat(self):
print('凶猛东北虎!')
cat = Cat('妙妙',3)
dog = Dog('旺财',2)
tiger = Tiger()
p = Pet('山竹',1)
person = Person('JJ')
person.feed_pet(cat) #此时涉及到了多态
person.feed_pet(tiger)
person.feed_pet(p)
输出:
JJ喜欢养宠物:猫,昵称是:妙妙
这个不是宠物!可能有危险!!!
JJ喜欢养宠物:Pet,昵称是:山竹
注意:
1.对同一个函数feed_pet(),传入不同参数(对象),可以实现不同功能。
2.由于python中所有类均继承于祖先类Object,故多态总能应用到很多场景(无论类与类之间是否有继承关系)。