Fork me on GitHub

python3面向对象

类的定义

面向对象两个核心:类、对象

在python中使用class定义类:

1
2
3
4
5
6
7
8
9
class Student():
name = '' #定义变量
age = 0
def print_file(self): #定义函数
print('name:' + self.name)
print('age:' + str(self.age))
class StudentHomework():
homework_name = ''

使用类需要对类进行实例化

1
2
student = Student()
student.print_file() #调用类下面的方法

类的最基本作用就是在封装代码。
类的内部不能调用类的方法,类只负责去描述和定义,而不负责执行,运行和调用类需要放在类的外部。
不推荐在一个模块中既定义类又使用类。

浅谈函数与方法的区别

在一个模块中调用另一个模块的类:

1
2
3
4
5
#nine.c2.py
from c1 import Student
Student = Student()
Student.print_file()

方法和函数的区别:

  • 方法是设计层面上的称谓,更多的是面向对象中的概念;
  • 函数是程序运行的,面向过程的一种称谓。
  • 没有必要特别的去区分方法和函数。

变量出现在类中更多称为数据成员。

类与对象

类和对象是通过实例化关联在一起的。
类是现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。
数据成员刻画对象的特征,方法刻画对象的行为。
行为要属于主体。

1
2
3
4
5
class Student():
name = '' #定义变量
age = 0
def do_homework(self): #print_file不是学生为主体的行为
print('homework')

类被实例化了之后变成具体的对象,若不具体的话就不是对象。
类相当于模板,通过类产生不同的对象。

构造函数

实例化创造不同的对象。

1
2
3
4
5
6
7
8
9
10
student1 = Student()
student2 = Student()
student3 = Student()
print(id(student1))
print(id(student2))
print(id(student3))
#3269808
#3410256
#3410320

init()为构造函数

  • 构造函数的调用是自动进行的;
  • 我们可以去主动调用构造函数。
  • 对构造函数来说只能返回None。
1
2
3
4
5
6
7
8
9
class Student():
name = '' #定义变量
age = 0
def __init__(self,name,age):
name = name
age = age
student1 = Student('tai',18)
print(student1.name,student1.age) #返回了空字符串

区别模块变量与类中的变量

局部变量不会覆盖全局变量

1
2
3
4
5
c
c = 50
def add(x,y)
c = x + y #这个c不会覆盖全局变量的c
print(c)

不能把模块的机制与类的机制混淆

类变量与实例变量

  • 类变量:和类相关的变量
  • 实例变量:和对象相关联的变量
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
class Student():
name = '' #类变量
age = 0
def __init__(self,name,age):
self.name = name #定义实例变量的方法
self.age = age
student1 = Student('tai',18) #实例变量
student2 = Student('park',19)
self不能称作关键字,可以定义为任意你想定义的标识。
class Student():
name = '123'
age = 0
def __init__(self,name,age):
self.name = name
self.age = age
student1 = Student('tai',18)
student2 = Student('park',19)
print(student1.name)
print(student2.name)
print(Student.name)
#tai
#park
#123

类与对象的变量查找顺序

dict打印一个保存当前对象内所有变量的字典

1
2
3
4
5
6
7
8
9
10
11
12
class Student():
name = '123' #定义变量
age = 0
def __init__(self,name,age):
name = name #并没有赋值给student1
age = age
student1 = Student('tai',18)
print(student1.name,student1.age)
print(student1.__dict__)
#123 0
#{}

打印的是空字典,说明实例变量赋值没有成功。

尝试访问实例变量时:在实例变量列表里查找,如果没有会到类变量里面寻找。如果类中没有会到父类寻找。

类也也可打印dict

1
2
3
4
5
6
7
8
9
10
11
class Student():
name = '123'
age = 0
def __init__(self,name,age):
name = name
age = age
student1 = Student('tai',18)
print(Student.__dict__)
#{'__module__': '__main__', 'name': '123', 'age': 0, '__init__': <function Student.__init__ at
0x03A693D8>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

self与实例方法

python中要显示定义在方法列表里。
如果在类中定义实例方法的话,要固定在类中放上self;
调用实例方法时不需要传入self。
self可以改为任意值,但是python建议使用self:

1
2
3
def __init__(this,name,age):
this.name = name
this.age = age
  • self就是当前调用某一个方法的对象。当student1调用时,self指代student1。
  • self代表实例而不是类。

实例方法是实例可以调用的方法,和对象实例相关联。

在实例方法中访问实例变量与类变量

方法需要对变量做一系列运算改变变量的意义
方法也能不操作任何变量

在方法中访问变量:

1
2
3
4
5
6
7
8
9
10
11
class Student():
name = '123'
age = 0
sum = 0
def __init__(self,name,age):
self.name = name
self.age = age
print(self.name) #读取的是self.name
print(name) #读取的是形参的name
student1 = Student('tai',18)

在实例方法中访问类变量:

1
2
3
4
5
6
7
8
9
10
11
class Student():
name = '123'
age = 0
sum1 = 0
def __init__(self,name,age):
self.name = name
self.age = age
print(Student.sum1) #第一种访问类变量的方式
print(self.__class__.sum1) #第二种访问类变量的方式
student1 = Student('tai',18)

类方法

类变量的使用场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student():
name = '123'
age = 0
sum1 = 0
def __init__(self,name,age):
self.name = name
self.age = age
self.__class__.sum1 += 1
print("当前班级人数为" + str(self.__class__.sum1))
student1 = Student('tai',18)
student2 = Student('tai',18)
student3 = Student('tai',18)
#当前班级人数为1
#当前班级人数为2
#当前班级人数为3

定义类方法:

1
2
3
@classmethod #装饰器
def plus_sum(cls): #class简写,cls代表调用类方法的类
pass

类方法里操作类变量更为简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Student():
name = '123'
age = 0
sum1 = 0
def __init__(self,name,age):
self.name = name
self.age = age
#self.__class__.sum1 += 1
#print("当前班级人数为" + str(self.__class__.sum1))
@classmethod
def plus_sum(cls):
cls.sum1 += 1
print(cls.sum1)
student1 = Student('tai',18)
Student.plus_sum() #调用类方法
student2 = Student('tai',18)
Student.plus_sum()
student3 = Student('tai',18)
Student.plus_sum()

实例方法关联对象,类方法关联类本身。

可以用对象调用类方法:

1
2
student1 = Student('tai',18)
student1.plus_sum() #可以,但是最好不要,在逻辑上说不通

静态方法

静态方法格式:

1
2
3
4
@staticmethod #装饰器
def add(x,y): #没有指代
print(Student.sum1) #可以访问类变量
print('This is a static method')

静态方法能够被类和对象调用:

1
2
3
student1 = Student('tai',18)
student1.add(1,3) #被对象调用
Student.add(1,4) #被类调用

静态方法和类方法种无法访问实例变量。

  • 能用静态方法的时候都可用类方法替代,最好不要经常使用,与类和对象的关联性非常弱,和普通的函数几乎没有区别。

成员可见性:公开和私有

成员可以简单的理解为变量和方法。
类是由内外之分的。

方法的内部调用:

1
2
3
4
5
6
def do_homework(self):
self.do_english_homework()
print('homework')
def do_english_homework(self):
print('english')

成员需要有一定的调用层级,以打分为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student():
name = '123'
age = 0
sum1 = 0
score = 0
def __init__(self,name,age):
self.name = name
self.age = age
self.score = 0
def marking(self,score):
if score < 0: #可以对数据进行判断
score = 0
self.score = score
print(self.name + '本次考试分数为' + str(self.score))
student1 = Student('tai',18)
student1.marking(59) #利用marking方法打分
  • 利用方法做数据修改比直接做数据修改要安全很多,因为通过方法可以判断。
  • 规范:如果要修改类的值应该全部通过方法来完成,可以保护数据。
  • 目前score是公开的public——类的外部可以直接访问。
  • 私有的private——不可以直接访问。
  • 不加双下划线:公开
  • 加双下划线:私有
  • 前后都有双下划线:公开,是python内置函数的命名风格。

没有什么是不能访问

尝试从外部访问私有变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():
def __init__(self,name,age):
self.name = name
self.age = age
self.__score = 0
def marking(self,__score):
if __score < 0:
__score = 0
self.__score = __score
print(self.name + '本次考试分数为' + str(self.__score))
student1 = Student('tai',18)
student1.marking(59)
student1.__score = -1 #没有报错
print(student1.__score)

再实例化之后尝试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student():
def __init__(self,name,age):
self.name = name
self.age = age
self.__score = 0
def marking(self,__score):
if __score < 0:
__score = 0
self.__score = __score
print(self.name + '本次考试分数为' + str(self.__score))
student1 = Student('tai',18)
student2 = Student('park',19)
student1.marking(59)
student1.__score = -1 #实际上是利用python动态特性,给student1添加了一个新的属性叫__score,并不是实例变量中的__score
print(student1.__score)
print(student2.__score) #报错了

利用dict查看:

1
2
3
4
5
6
7
8
9
student1 = Student('tai',18)
student2 = Student('park',19)
student1.marking(59)
student1.__score = -1
print(student1.__dict__)
print(student2.__dict__)
#{'name': 'tai', 'age': 18, '_Student__score': 59, '__score': -1}
#{'name': 'park', 'age': 19, '_Student__score': 0}

多出了一个_Studentscore变量(是之前的私有变量score)。

  • 尝试访问私有变量时报找不到私有变量的错误,是因为python已经把私有变量的名字给修改了。
  • Python私有变量的保护机制其实就是将私有变量的名字更换之后使你无法从外部读取到。
  • 不可以动态添加私有变量。动态添加的私有变量会存在于dict列表中,既然在dict中就可以读取,失去了其作为私有变量的特性。

由此可以向下延申:单下划线 + 类名 + 双下划线 + 实例变量名
通过这种方式可以间接的读取私有变量:

1
2
3
4
5
6
student1 = Student('tai',18)
student2 = Student('park',19)
student1.marking(59)
student1.__score = -1
print(student1._Student__score) #读取到了私有变量

继承

面向对象三大特性:继承性、封装性、多态性。

  • 最难讲清楚的反而是封装性,而继承性和多态性都是语法问题。
  • 继承性:避免我们定义重复的方法和重复的变量。
  • 建议一个模块写一个类。

第一种继承方法:

1
2
3
4
5
6
7
8
#nine.c3.py
class People():
pass
#nine.c1.py
import c3
class Student(c6.Pelple): #继承people,不建议这么写
pass

第二种继承方法:

1
2
3
from c3 import People
class Student(Pelple):
pass

继承测试:

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
#nine.c3.py
class Human():
sum = 0
def __init__(self,name,age):
self.name = name
self.age = age
def get_name(self):
print(self.name)
#nine.c1.py
from c3 import Human
class Student(Human):
pass
print(Student.sum) #通过类调用类变量
student1 = Student('tai',18) #继承至父类Human的__init__,需要录入name和age两项
print(student1.sum) #通过对象调用类变量
print(student1.name)
print(student1.age)
student1.get_name()
#0 #sum能打印出值来
#0
#tai
#18
#tai #方法也可以继承
  • 子类没有sum,由于其继承了父类的sum,所有能够打印出来。

  • python允许多继承,一个子类可以有多个父类。

在子类种调用父类的构造函数(不推荐):

1
2
3
4
5
from c3 import Human
class Student(Human):
def __init__(self,school,name,age):
self.school = school
Human.__init__(self,name,age) #调用父类构造函数

在这里必须要传入self才不会报错。

子类方法调用父类方法:super关键字

一个类去调用一个实例方法实际上是不正确的。

1
2
3
4
5
6
7
8
9
10
from c3 import Human
class Student(Human):
def __init__(self,school,name,age):
self.school = school
Human.__init__(self,name,age) #调用父类初始化要加self
def do_homework(self):
print('done')
student1 = Student('春田花花幼儿园','tai',18) #调用构造函数不需要加self
student1.do_homework() #通过对象调用不用写self

实例化的时候调用构造函数时是python内部构造机制自动调用构造函数,它会帮助我们补全前面的self;
而在子类init中调用父类的init是自己通过类(而不是实例化的对象)去调用,python不会帮你补全前面的self。
如果通过对象调用,self的实质就是实例对象,所以不要写self。

验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from c3 import Human
class Student(Human):
def __init__(self,school,name,age):
self.school = school
Human.__init__(self,name,age)
def do_homework(self):
print('done')
student1 = Student('春田花花幼儿园','tai',18)
Student.do_homework() #通过类去调用实例方法
#Traceback (most recent call last):
# File "c1.py", line 13, in <module>
# Student.do_homework()
#TypeError: do_homework() missing 1 required positional argument: 'self'
#报错,缺少self。

尝试传值进去:

1
2
3
4
5
6
7
8
9
10
from c3 import Human
class Student(Human):
def __init__(self,school,name,age):
self.school = school
Human.__init__(self,name,age)
def do_homework(self):
print('done')
student1 = Student('春田花花幼儿园','tai',18)
Student.do_homework(' ') #传入空字符串,类调用实例方法成功

其实此时def do_homework(self)中的self已经变成一个普通参数,传什么值进去都可以。

  • 开闭原则:对代码扩展开放,对代码更改关闭。

不应更换父类就更换子类中的代码,使用super调用父类:

1
2
3
4
5
from c3 import Human
class Student(Human): #使用super后只需要在此将父类名称更换即可
def __init__(self,school,name,age):
self.school = school
super(Student,self).__init__(name,age)

不仅构造函数可以使用super,其他方法也可以使用:

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
#nine.c3.py
class Human():
sum = 0
def __init__(self,name,age):
self.name = name
self.age = age
def get_name(self):
print(self.name)
def do_homework(self):
print('this is a father message')
#nine.c1.py
from c3 import Human
class Student(Human):
def __init__(self,school,name,age):
self.school = school
#Human.__init__(self,name,age)
super(Student,self).__init__(name,age)
def do_homework(self):
super(Student,self).do_homework()
print('done')
student1 = Student('春田花花幼儿园','tai',18)
student1.do_homework()
#this is a father message #成功调用了父类的方法
#done

子类和父类方法同名会优先调用子类方法。

-------------本文结束 感谢您的阅读-------------
  • 本文标题: python3面向对象
  • 本文作者: 吴军旗
  • 本文链接: http://fanqieto.top/2018/07/17/python3面向对象/
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请保留原文链接及作者!
    • 欢迎搜索及关注:番茄技术小栈,所有文章都将同步在公众号上!
坚持原创技术分享,您的支持将鼓励我继续创作!