Python 的封装学习。

例题引导:构造一个名为Vector的类,用来储存一个平面坐标系中的向量(例如(1,2));

在类中构造向量的相加、相减和相乘三种方法,并分别返回计算结果的向量;

再构造一个输出向量的方法,按照(x,y)的格式输出向量;

最后使用main函数输入两个向量,输出他们的加、减、乘计算结果。

解题思路:关于类的创建以及函数的创建及调用

参考答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Vector(object):
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other): #加
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)

def __sub__(self, other): #减
x = self.x - other.x
y = self.y - other.y
return Vector(x, y)

def __mul__(self, times): #乘
return Vector(self.x * times, self.y * times)

def __repr__(self): #格式化输出
return 'Vector({}, {})'.format(self.x, self.y)

def main():
v1 = Vector(3, 5)
v2 = Vector(4, 5)
v3 = v1 + v2
v4 = v3 * 2
v5 = v2 - v1
print(v3)
print(v4)
print(v5)


if __name__ == '__main__':
main()

学习引导:

  • 类定义
  • 类对象
  • 类属性与方法

类和对象

为了更好的理解本节内容,你首先需要了解什么是面向对象。

python语言是一种面向对象的语言,因此在python中创建一个类和对象是很容易的。

下面简单介绍一下面向对象中的一些基本概念:

  • 类(class):类是抽象的概念,它是一种数据结构,就好比一个模型,该模型是生活中具有相同属性(特征)和方法(动作)的同一类事务,可以用它来生产真实的物体(实例),对象是类的实例。

  • 对象:对象就是具有类属性和方法的具体事物(就是基于类而创建的一个具体的事物),python中一切皆对象。对象包括两个数据成员(类变量和实例变量)和方法。对象的属性一般指主要的特征和参量,而方法主要指函数。

  • 类与对象的关系:对象是有类产生的。一个类为它的全部对象给出了一个统一的定义,而他的每个对象则是符合这种定义的一个实体,因此类和对象的关系就是抽象和具体的关系

  • 实例化:创建一个类的实例,类的具体对象。

  • 实例化对象:由类生产的一个具体对象就叫实例化对象,也就是对象。

  • 方法:类中定义的函数。

  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

  • 数据成员:类变量或实例变量用于处理类及其实例对象的相关数据。

  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程称为方法的覆盖(override重写)。

  • 局部变量:定义在方法中的变量,只作用于当前实例的类。

  • 实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用self修饰的变量。

  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。

和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制。

Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。

对象可以包含任意数量和类型的数据。

面向对象编程的特征:

  • 封装:对外部隐藏对象的工作细节

  • 继承:子类自动共享父类之间数据和方法的机制

  • 多态:可以对不同类的对象调用相同的方法,产生不同的结果

类定义

类实例化后,可以使用其属性。创建一个类之后,可以通过类名访问其属性。

语法格式如下:

1
2
3
4
5
6
7
class ClassName(): #类的命名一般首字母大写,括号中参数可带可不带,带只能带继承的基类名称
'这里可写类的说明文档'
<statement-1>
.
.
.
<statement-N>

类对象

类对象支持两种操作:属性引用和实例化。

属性引用:使用和python中所有的属性引用一样的标准语法:obj.name。

类对象创建后,类命名空间中所有的命名都是有效属性名。

1
2
3
4
5
6
7
8
9
10
11
12
class MyClass:
"演示一个简单的类实例"
i='DataScience'
def func(self):
return 'Hello World'

#实例化类
x=MyClass()

#访问类的属性和方法
print('MyClass类的属性i为:',x.i)
print('MyClass类的方法func输出为',x.func())
1
2
#MyClass类的属性i为: DataScience
#MyClass类的方法func输出为 Hello World

类有一个名为 __ init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用。

语法格式如下:

1
2
def __init__(self):
self.data=[]

__ init__ () 方法可以有参数,参数通过 __ init__() 传递到类的实例化操作上。例如:

1
2
3
4
5
6
7
class Special:
def __init__(self,realpart,imagpart):
self.r=realpart
self.i=imagpart

x=Special('welcome',12)
print(x.r,x.i)
1
#welcome 12

self

self 代表类的实例,而非类。

类的方法与普通的函数有一个区别—它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。

1
2
3
4
5
6
7
class Test:
def test(self):
print(self)
print(self.__class__)

t=Test()
t.test()
1
2
#<__main__.Test object at 0x000002698B8C56C8>
#<class '__main__.Test'>

从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。

self 不是 python 关键字,我们把他换成 datascience 也是可以正常执行的:

1
2
3
4
5
6
7
class Test:
def test(datascience):
print(datascience)
print(datascience.__class__)

t = Test()
t.test()
1
2
#<__main__.Test object at 0x000002698B8CC448>
#<class '__main__.Test'>

类属性与方法

  • 类中的函数叫方法

  • 类中的变量叫属性

类的私有属性

__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。
在类内部的方法中使用时self. private_attrs。

类的方法

在类的内部,使用def关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数,self代表的是类的实例。

注意:self这个名字不是定死的,也可以使用this,但最好还是用常用的self。

类的私有方法

__ private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self. __private_methods。

实例

私有属性的实例如下“

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PrivateEX:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量

def count(self):
self.__secretCount += 1
self.publicCount += 1
print (self.__secretCount)

counter = PrivateEX()
counter.count()
counter.count()
print (counter.publicCount)
print (counter.__secretCount) # 报错,实例不能访问私有变量
1
2
3
4
5
6
7
8
9
10
11
12
13
#1
#2
#2

---------------------------------------------------------------------------

AttributeError Traceback (most recent call last)

<ipython-input-1-f0d64b9d6654> in <module>
12 counter.count()
13 print (counter.publicCount)
---> 14 print (counter.__secretCount) # 报错,实例不能访问私有变量
AttributeError: 'PrivateEX' object has no attribute '__secretCount'

类的私有方法实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Private:
def __init__(self, name, url):
self.name = name # public
self.__url = url # private

def who(self):
print('name : ', self.name)
print('url : ', self.__url)

def __foo(self): # 私有方法
print('这是私有方法')

def foo(self): # 公共方法
print('这是公共方法')
self.__foo()

x = Private('DataScience', 'https://www.a2data.cn/#indexCard')
x.who() # 正常输出
x.foo() # 正常输出
x.__foo() # 报错 外部不能调用私有方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'''
name : DataScience
url : https://www.a2data.cn/#indexCard
这是公共方法
这是私有方法
'''

---------------------------------------------------------------------------

AttributeError Traceback (most recent call last)

<ipython-input-8-0696acdddb15> in <module>
18 x.who() # 正常输出
19 x.foo() # 正常输出
---> 20 x.__foo() # 报错 外部不能调用私有方法
AttributeError: 'Private' object has no attribute '__foo'

通过类的实例化,可以创建多个实例对象。

下面的例子为多个对象的实例化:

1
2
3
4
5
6
7
8
9
class my_class():
num=100
def print_num(self):
print('num is %d'%(self.num))

class_1=my_class() #实例化
class_2=my_class() #实例化
class_1.print_num()
class_2.print_num()
1
2
#num is 100
#num is 100
1
2
3
4
class_1.num=10  #修改实例属性
my_class.num=50 #修改类属性
class_1.print_num()
class_2.print_num()
1
2
#num is 10
#num is 50
1
2
class_1.num2=5  #新增类属性
print(class_1.num2)
1
#5

类增加/修改属性

从外部添加/修改属性

1
2
3
4
class  Example():
pass
Example.num=100
print(Example.num)
1
#100

从内部添加/修改属性

1
2
3
4
5
6
7
class Example():
@classmethod
def add_num(cls):
cls.num=input('请输入:')
print(cls.num)

Example.add_num()
1
2
#请输入:2
#2

在这里我们介绍一下上段代码所用到的@classmethod

一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。

而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁,既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢?

现从他们使用区别来看

  • @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。

  • @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。

  • 如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。

  • 而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

  • 类方法不但能调用类属性,还能直接调用类中的其他方法

下面为一个成绩录入的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class List():
@classmethod
def enter(cls):
cls.name=input('请输入学生姓名:')
cls.grade=int(input('请输入考试成绩:'))

@classmethod
def count(cls):
if cls.grade>=60:
return '及格'
else:
return '不及格'

@classmethod
def result(cls):
print(cls.count())

List.enter()
List.result()
1
2
3
4
5
'''
请输入学生姓名:xiaowang
请输入考试成绩:32
不及格
'''
  • 小作业:

本次的习题可能难度稍大,涉及后面的知识,大家可以慢慢来。

  1. 构造四个类,其需要包含的属性和方法如下:

    people类:公开的属性(名字,年龄),私有属性(体重),构造方法,普通方法(输出名字和年龄);

    student类(继承people类):公开的属性(年级),继承父类的构造方法并加入自身的属性,普通方法(输出名字、年龄和年级);

    speaker类:公开的属性(演讲主题,姓名),构造方法,普通方法(输出名字和演讲主题);

    introduction类(同时继承speaker和student):继承父类的构造方法。

    尝试调用你的函数中各类和方法。

  2. 开发一把枪:
    1)判断是否有子弹,没有子弹无法射击

    2)使用 print 提示射击,并且输出子弹数量

  3. 承接上面的枪的类,现改良ak47加强版,冒蓝火的,装弹量增加30

评论