0%

python进阶学习三(面向对象)

面向对象概念

和java一样,类(class)和对象是面向对象的体现,它是一种设计思想现实生活在计算机世界的映射。类的最基本作用就是封装代码,在一个类里可以定义很多个类。

1,类的定义

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
35
class Student():
#可以在类体定义变量
name = ''
age = 0
c = 9

#也可以在类体定义函数
def print_file(self): #要加self
def print_file(self):
print('name:' + name)
print('name:' + str(age)
#然而运行还是会报错
PS G:\python> python test10.py
Traceback (most recent call last):
File "test10.py", line 15, in <module>
student.print_file()
File "test10.py", line 11, in print_file
print('name:' + name)
NameError: name 'name' is not defined
#显示没有定义 name变量
#正确的方法是在name和age这两个变量前加self.来引用。
class Student():
name = ''
age = 0

def print_file(self):
print('name:' + self.name)
print('name:' + str(self.age))

student = Student()
student.print_file()
#运行结果
PS G:\python> python test10.py
name:
name:0

1.1 方法与函数的区别

1,实际上很多时候我们都把他们划等号,java、c#一般称为方法,c和c++一般称函数。
2,方法:趋向于面向对象的概念,python建议称方法,在模块里就建议称函数。
3,函数:面向过程的概念
当然没有必要刻意强调它们的区别

1.2 变量

1,在模块中定义的就称变量
2,在类中定义的就称为数据成员(这样以体现封装性)。

1.3 类和对象的关系和区别

类的定义:类是现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。
类只是一类事物的总称,并不是一个具体的集合,具体的对象表示具体的类的一个实例,类的实例化就生成一个对象。

1.4 类的实例化

1
2
3
4
5
6
7
8
9
10
11
student1 = Student() 
student2 = Student()
student3 = Student()
print(id(student1))
print(id(student2))
print(id(student3))
#运行结果,可以看到他们的内存地址都是不同的
PS G:\python> python test10.py
2479712232448
2479712232840
2479712232616

构造函数只能返回none,不能返回其他类型的值,比如字符串。
我们在实例化对象的时候要在构造函数传入定义的变量,然后赋值,在生成对象的时候传入我们要定义的实际参数的值,

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
class Student():
name = ''
age = 0
def __init__(self,name,age):
#这是构造函数
name = name
age = age

def print_file(self):
print('name:' + self.name)
print('name:' + str(self.age))

student1 = Student('石头',18)
print(student1.name)
#打印
PS G:\python> python test10.py

#但是输出却是空,为什么?再看一个小例子:
c = 50

def add(x,y):
c = x + y
print(c)

add(1,2)
print(c)
#输出结果
PS G:\python> python test11.py
3
50

这里我们在函数内部打印c的值是3,在外部打印c的值是50,在函数里面c是局部变量,不会改变全局变量的值,不过类的情况不同。下面分析一下。

1.5 类变量和实例变量

类变量时和类相关联的,实例变量是和对象相关联的,python里面是用self.变量名来保存实例变量的值。如下代码所示,

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
35
36
37
38
39
40
41
42
43
44
45
class Student():
name = ''
age = 0

def __init__(self,name,age):
#这是构造函数
self.name = name
self.age = age

def print_file(self):
print('name:' + self.name)
print('name:' + str(self.age))

student1 = Student('石头',18)
# student1 = Student('石敢',18)
print(student1.name,student1.age)
#运行结果
PS G:\python> python test10.py
石头 18
#再看如下代码
class Student():
#不适合定义在这里
name = 'qiyue'
age = 0

def __init__(self,name,age):
#这是构造函数
self.name = name
self.age = age

def print_file(self):
print('name:' + self.name)
print('name:' + str(self.age))

student1 = Student('石头',18)
student2 = Student('石敢',18)
print(student1.name)
print(student2.name)
print(Student.name)
#运行结果
PS G:\python> python test10.py
石头
石敢
qiyue
两个对象的name是不同的,所以打印出不同的结果,最后一行打印出qiyue,这是类变量,但是这样没有意义,名字和年龄应该定义成实例变量,而不是定义在如上代码的位置,因为类不是具体的一个对象。

下面分析一下类变量和实例变量的有关问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():

name = 'qiyue'
age = 0

def __init__(self,name,age):
name = name
age = age

student1 = Student('石头',18)
print(student1.name)
#打印
PS G:\python> python test10.py
qiyue
qiyue

如上代码所示,我们打印对象student1的name是qiyue,直接打印类的变量name值也是qiyue,后者好理解,可是我们为什么试图打印student1的name也是qiyue而不是石头呢?

1
2
3
def __init__(self,name,age):
name = name
age = age

我们这样写是不会给对象赋值的,应该配合self来使用。

1
2
3
def __init__(self,name,age):
self.name = name
self.age = age

self

如果定义的是实例方法,那我们必须在方法的参数列表第一个固定加上self,这是python的规定(实际上也可以用this,不过建议self),要显式的传入self,self就代表当前对象,不过我们调用实例方法的时候不需要传self,代码:

1
2
3
4
5
6
7
8
9
10
11
#定义的时候要在参数列表第一个位置加self
class Student():

name = 'qiyue'
age = 0
def __init__(self,name,age):
self.name = name
self.age = age

#调用的时候则不需要这么做
student1 = Student('石头',18)

如果要在实例方法中和函数外访问类变量,应该用类名.变量名调用。方法一

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 Student():
sum1 = 0
name = 'qiyue'
age = 0

def __init__(self,name,age):

self.name = name
self.age = age
print(Student.sum1)

student1 = Student('石头',18)
print(student1.name)
print(Student.sum1)

class Student():
sum1 = 0
name = 'qiyue'
age = 0

def __init__(self,name,age):

self.name = name
self.age = age
print(Student.sum1)

student1 = Student('石头',18)
print(student1.name)
print(Student.sum1)
#运行结果
PS G:\python> python test10.py
0
石头
0

方法二:通过self,self有一个自带的class,它就是Student类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student():
sum1 = 0
name = 'qiyue'
age = 0

def __init__(self,name,age):

self.name = name
self.age = age
print(self.__class__.sum1)

student1 = Student('石头',18)
print(student1.name)
print(Student.sum1)
#打印结果
PS G:\python> python test10.py
0
石头
0

2,类方法

为什么会有类方法?它有什么作用?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Student():
sum = 0
# name = 'qiyue'
# age = 0

def __init__(self, name, age):
self.name = name
self.age = age
self.__class__.sum += 1
print('当前班级学生总数为:' + str(self.__class__.sum))

def do_homework(self):
print('do homework')

student1=Student('小明', 18)
student1=Student('小强', 18)
student1=Student('小黄', 18)
#运行结果
PS G:\python> python test10.py
当前班级学生总数为:1
当前班级学生总数为:2
当前班级学生总数为:3

类变量也有一个专门操作它的方法,叫类方法

1
2
3
@classmethod
def plus_sum(cls):
pass

这是类方法定义的规范,在方法前面添加一个装饰器@classmethod。类方法怎么调用呢?

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
class Student():
sum = 0
# name = 'qiyue'
# age = 0

def __init__(self, name, age):
self.name = name
self.age = age
self.__class__.sum += 1
print('当前班级学生总数为:' + str(self.__class__.sum))

@classmethod
def plus_sum(cls):
cls.sum += 1
print(cls.sum)

student1=Student('小明', 18)
Student.plus_sum()
student1=Student('小强', 18)
Student.plus_sum()
student1=Student('小黄', 18)
Student.plus_sum()
#打印
PS G:\python> python test10.py
当前班级学生总数为:1
2
当前班级学生总数为:3
4
当前班级学生总数为:5
6

实例化对象的时候,对sum操作 加1,然后在类方法中又对sum进行加1操作,所以会如上代码打印。cls可以 叫别的名字吗?可以,我们换成self也同样可以。实例方法关联的是对象,类方法关联的是类。这是区别,既然可以在实例方法中操作类变量,那还要类方法做什么?因为在操作与对象无关的变量,最正确的方法还是用类方法(实例方法也可以,但是不建议)。
self和cls
self是可以代表当前对象的一个参数;cls是代表当前类的一个参数,代表当前类。
也可以用对象调用类的方法(不建议,java中就不可以这么做)。

3,静态方法

定义:

1
2
3
@staticmethod
def add(x,y):
print('This is a static method')

调用:

1
2
3
4
5
6
7
8
9
...
student1=Student('小明', 18)
student1.add(1,2)
Student.add(1,2)
...
#运行结果
PS G:\python> python test10.py
This is a static method
This is a static method

注:省略了部分代码。静态方法可以用类和对象调用。静态方法也可以访问类变量。静态方法不建议经常用。

4,成员的可见性

面向对象语言都有这个概念,java使用private、public来修饰,也叫权限修饰符。python是怎么定义一个方法的权限呢?:双下划线。
在方法前面加双下划线。如:

1
2
3
4
5
def __marking(self,score):
if score < 0:
return '分数不合法,请重新打分'
self.score = score
print(self.name + '同学本次考试分数为:' + str(self.score))

这里有个疑问,构造函数也是前面有双下划线为什么不是私有的?仔细看,init后面还有双下划线,在python里,函数名前后都有双下划线就不会被认为是私有函数,我们可以运行函数来证明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
def marking(self,score):
if score < 0:
return '分数不合法,请重新打分'
self.score = score
print(self.name + '同学本次考试分数为:' + str(self.score))
...
student1=Student('小明', 18)
result = student1.marking(-9)
print(result)
#运行结果
PS G:\python> python test10.py
分数不合法,请重新打分
#这里运行了marking方法,我们在前面加双下划线,运行结果如下:
PS G:\python> python test10.py
Traceback (most recent call last):
File "test10.py", line 50, in <module>
result = student1.marking(-9)
AttributeError: 'Student' object has no attribute 'marking'
#果然,现在不能访问了,"no attribute 'marking'",我们再在marking后面加双下划线 "__marking__",则可以调用。当然这样不建议,因为它是python内置的定义方式。

我们可以强制访问,在调用的时候在变量前面加双下划线。如: student1.__score = 1.

1
2
3
4
5
6
7
8
9
student1=Student('小明', 18)
result = student1.marking(9)
student1.__score = -9
print(student1.__score)
#打印
PS G:\python> python test12.py
小明同学本次考试分数为:59
-1
{'name': '小明', 'age': 18, '_Student__score': 59, '__score': -1}

注意 _Student__score ,它就是我们定义的私有变量score,这是python私有变量的保护机制。为什么我们访问不到这个score变量,实际上就是python修改了它的名字,前面加上单下划线和类名。严格意义上python并没有私有变量。我们还是可以访问所谓的私有变量,”students._Student__score”。

1
2
3
4
5
6
7
8
9
10
11
student1=Student('小明', 18)
student2=Student('小兰', 18)
result = student1.marking(59)
student1.__score = -1
print(student1.__score)
print(students._Student__score)
#打印
PS G:\python> python test12.py
小明同学本次考试分数为:59
-1
0