我是靠谱客的博主 风中泥猴桃,这篇文章主要介绍9、类的封装和继承,现在分享给大家,希望可以做个参考。

1、数据封装

class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score

在上面的Student类中,每个实例就拥有各自的namescore这些数据。我们可以通过函数来访问这些数据,比如打印一个学生的成绩:

>>> def print_score(std):
...
print('%s: %s' % (std.name, std.score))
...
>>> print_score(bart)
Robot: 59

但是,既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法:

class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))

要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入:

>>> bart.print_score()
Bart Simpson: 59

这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出namescore,而如何打印,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

封装的另一个好处是可以给Student类增加新的方法,比如get_grade

class Student(object):
...
def get_grade(self):
if self.score >= 90:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'

2、访问控制

Python中没有访问控制的关键字,例如privateprotected(java)等等。但是,在Python编码中,有一些约定来进行访问控制。

单下划线 "_"

在Python中,通过单下划线 "_" 来实现模块级别的私有化,变量除外一般约定以单下划线”“开头的函数为模块私有的,也就是说”from moduleName import * 将不会引入以单下划线 "_" 开头的函数。

现在有一个模块 example_example.py,内容用如下,模块中一个变量名和一个函数名分别以"_"开头:

# example_example.py
name = 'bruce'
_tall = 180
def call_for():
print('name is :',name)
print('_tall is',_tall)
def _call_for():
print('name is :',name)
print('_tall is',_tall)
# _tall is 180

再次调用该脚本:

# 调用脚本模块example_example
import example_example
# 调用不带下划线变量
example_example.name
Out[12]: 'bruce'
# 调用带下划线变量
example_example._tall
# 对于变量单下划线不会影响调用
Out[13]: 180
# 调用不带下划线函数
example_example.call_for()
Out[16]:
name is : bruce
_tall is 180
# 调用带下划线函数会报错
example_example._call_for()
Traceback (most recent call last):
File "<ipython-input-15-e642b1386946>", line 1, in <module>
example_example._call_for()
TypeError: 'NoneType' object is not callable

双下划线 "__"

  • 对于Python中的类属性,可以通过双下划线 "__"来实现一定程度的私有化,因为双下划线开头的属性在运行时会被”混淆”(mangling)。
class Person(object):
tall = 180
hobbies = []
def __init__(self, name, age,weight):
self.name = name
self.age = age
self.weight = weight
self.__Id = 430
@classmethod
def infoma(cls):
print(cls.__Id)
# person.infoma()
Bruce = person("Bruce", 25,180)
print(Bruce.age)
print(Bruce.__Id)

运行结果:

25
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-32-ae0c1d7abe5a> in <module>()
15 Bruce = Person("Bruce", 25,180)
16 print(Bruce.age)
---> 17 print(Bruce.__Id)
AttributeError: 'person' object has no attribute '__Id'

其实,通过内建函数 dir() 就可以看到其中的一些原由,"__address" 属性在运行时,属性名被改为了"_Person__address"(属性名前增加了单下划线和类名

print(dir(Bruce))
#可以看到Bruce中有_person__Id的属性,相较原__Id属性,变得可调用
[ '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'_Person__Id', 'age', 'hobbies', 'infoma', 'name', 'tall', 'weight']

所以说,即使是双下划线,也没有实现属性的私有化,因为通过下面的方式还是可以直接访问"__address"属性:

print(Bruce._Person__Id)
430
# 通过使属性__Id名前增加了单下划线_和类名person来实现属性的可调用
  • 双下划线的另一个重要的目地是,避免子类对父类同名属性的冲突
class A(object):
def __init__(self):
self.__private()
self.public()
def __private(self):
print('A.__private()')
def public(self):
print('A.public()')
class B(A):
def __private(self):
print('B.__private()')
def public(self):
print('B.public()')
b = B()
# A.__private()
# B.public()

当实例化B的时候,由于没有定义__init__ 函数,将调用父类的__init__,但是由于双下划线的”混淆”效果,"self.__private()"将变成 "self._A__private()"

总结:

"_""__"的使用 更多的是一种规范/约定,并没有真正达到限制的目的:

  • "_":以单下划线开头的表示的是 protected 类型的变量,即只能允许其本身与子类进行访问;同时表示弱内部变量标示,如,当使用from moduleNmae import *时,不会将以一个下划线开头的对象引入。
  • "__":双下划线的表示的是私有类型的变量。只能是允许这个类本身进行访问了,连子类也不可以,这类属性在运行时属性名会加上单下划线和类名。

3、继承

(1)继承

在Python中,同时支持单继承与多继承,一般语法如下:

class SubClassName(ParentClass1 [, ParentClass2, ...]):
class_suite

实现继承之后,子类将继承父类的属性,也可以使用内建函数insubclass()来判断一个类是不是另一个类的子孙类

class Parent(object):
'''
parent class
'''
numList = []
def numdiff(self, a, b):
return a-b
class Child(Parent):
pass
c = Child()
# 子类继承父类的属性

Child.numList.extend(range(10))
print(Child.numList)
print("77 - 2 =", c.numdiff(77, 2))
print(issubclass(Child, Parent))
print(issubclass(Child, object))
# bases属性查看父类
print('the bases are:',Child.__bases__)
# doc属性不会被继承
print(Parent.__doc__)
print(Child.__doc__)

运行结果:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
77 - 2 = 75
True
True
the bases are: (<class '__main__.Parent'>,)
parent class
None

例子中唯一特别的地方是文档字符串。文档字符串对于类,函数/方法,以及模块来说是唯一的,也就是说doc属性是不能从父类中继承来的。

(2)继承中的__init__

当在Python中出现继承的情况时,一定要注意初始化函数__init__的行为:

  • 如果子类没有定义自己的初始化函数,父类的初始化函数会被默认调用;但是如果要实例化子类的对象,则只能传入父类的初始化函数对应的参数,否则会出错。
  • 如果子类定义了自己的初始化函数,而在子类中没有显示调用父类的初始化函数,则父类的属性不会被初始化
  • 如果子类定义了自己的初始化函数,在子类中显示调用父类,子类和父类的属性都会被初始化

子类没有定义自己的初始化函数,父类的初始化函数会被默认调用:

# 定义父类:Parent
class Parent(object):
def __init__(self, name):
self.name = name
print("create an instance of:", self.__class__.__name__)
print("name attribute is:", self.name)
# 定义子类Child ,继承父类Parent

class Child(Parent):
pass
# 子类实例化时,由于子类没有初始化函数,此时父类的初始化函数就会默认被调用
# 且必须传入父类的参数name
c = Child("init Child")
# create an instance of: Child
# name attribute is: init Child

如果不传入父类的参数name

class Parent(object):
def __init__(self, name):
self.name = name
print("create an instance of:", self.__class__.__name__)
print("name attribute is:", self.name)
class Child(Parent):
pass
c = Child()

没有传入父类name参数的输出结果会报错:

TypeError Traceback (most recent call last)
<ipython-input-4-ed134385adc7> in <module>()
8
pass
9
---> 10 c = Child()
TypeError: __init__() missing 1 required positional argument: 'name'

子类定义了自己的初始化函数,而在子类中没有显示调用父类的初始化函数,则父类的属性不会被初始化

class Parent(object):
def __init__(self, name):
self.name = name
print("create an instance of:", self.__class__.__name__)
print("name attribute is:", self.name)
#子类继承父类

class Child(Parent):
#子类中没有显示调用父类的初始化函数
def __init__(self):
print("call __init__ from Child class")
#c = Child("init Child") 
#print()

#将子类实例化

c = Child()
print(c.name)

在子类中没有显示调用父类的初始化函数,则父类的属性不会被初始化,因而此时调用子类中name属性不存在:

AttributeError: ‘Child’ object has no attribute ‘name’
call __init__ from Child class
---------------------------------------------------------------------------
AttributeError
Traceback (most recent call last)
<ipython-input-6-a7f9bd486d25> in <module>()
12 #将子类实例化
13 c = Child()
---> 14 print(c.name)
AttributeError: 'Child' object has no attribute 'name'

如果子类定义了自己的初始化函数,显示调用父类,子类和父类的属性都会被初始化

class Parent(object):
def __init__(self, name):
self.name = name
print("create an instance of:", self.__class__.__name__)
print("name attribute is:", self.name)
class Child(Parent):
def __init__(self):
"""在子类的初始化函数中显示调用父类的初始化函数
必须传入父类初始化函数的对应参数
"""
print("call __init__ from Child class")
super(Child,self).__init__("data from Child")
# 要将子类Child和self传递进去
d = Parent('qiqi')
c = Child()
print(c.name)
# 实例化父类Parent的结果
create an instance of: Parent
name attribute is: qiqi
# 实例化子类Child的结果
call __init__ from Child class
# super首先会先使得父类初始化的参数进行实例化
create an instance of: Child
name attribute is: data from Child
data from Child

(3)super的使用详解

  • super主要来调用父类方法来显示调用父类,在子类中,一般会定义与父类相同的属性(数据属性,方法),从而来实现子类特有的行为。也就是说,子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的属性和方法
class Parent(object):
Value = "Hi, Parent value"
def fun(self):
print("This is from Parent")
# 定义子类,继承父类

class Child(Parent):
Value = "Hi, Child
value"
def ffun(self):
print("This is from Child")
c = Child()
c.fun()
c.ffun()
print(Child.Value)
This is from Parent
This is from Child
Hi, Child
value

但是,有时候可能需要在子类中访问父类的一些属性,可以通过父类名直接访问父类的属性,当调用父类的方法是,需要将”self”显示的传递进去的方式:

class Parent(object):
Value = "Hi, Parent value"
def fun(self):
print("This is from Parent")
class Child(Parent):
Value = "Hi, Child
value"
def fun(self):
print("This is from Child")
Parent.fun(self)
# 调用父类Parent的fun函数方法
c = Child()
c.fun()
This is from Child
This is from Parent

这种方式有一个不好的地方就是,需要经父类名硬编码到子类中,为了解决这个问题,可以使用Python中的super关键字:

class Parent(object):
Value = "Hi, Parent value"
def fun(self):
print("This is from Parent")
class Child(Parent):
Value = "Hi, Child
value"
def fun(self):
print("This is from Child")
# Parent.fun(self)
super(Child,self).fun()
# 相当于用super的方法与上一调用父类的语句置换,与Parent.fun(self)的效果是等价的
c = Child()
c.fun()
This is from Child
This is from Parent

最后

以上就是风中泥猴桃最近收集整理的关于9、类的封装和继承的全部内容,更多相关9、类内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(120)

评论列表共有 0 条评论

立即
投稿
返回
顶部