Python 入门(五)- 面向对象(上)

类与对象

类的作用在于封装代码,关键字为 class

实例化可以理解为用 class 为模版创建一个对象,实例化时无需 new 关键字。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student():
name = ''
age = 0

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

### 类的使用
# 实例化
student = Student()
# 调用类的方法
student.print_file()

注:
1.类名首字母大写,驼峰式命名
2.定义变量、函数
3.类中方法的参数必须有self,否则会报错。如下

1
2
3
4
Traceback (most recent call last):
File "main.py", line 11, in <module>
student.print_file()
TypeError: print_file() takes no arguments (1 given)

4.self.name中的self不可省略,否则会报错。如下

1
2
3
4
5
6
Traceback (most recent call last):
File "main.py", line 11, in <module>
student.print_file()
File "main.py", line 6, in print_file
print('name:' + name)
NameError: global name 'name' is not defined

5.类只用于定义,不可在类中调用和运行函数
6.函数与方法没有绝对的区别,通常在类中的函数称之为“方法”
7.类与对象通过实例化进行关联,类实例化后即为对象
8.对象中的内容相同,但是对象的内存存储位置不同。通过参数,可将对象不同,如下例。

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

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

student1 = Student()
student2 = Student()
student3 = Student()

print(id(student1)) # 4500700192 内存地址
print(id(student2)) # 4500700336
print(id(student3)) # 4500700408

类与对象知识主干

1
2
3
4
5
6
7
8
9
10
11
12
13
14
python 类
|- 变量
|- 类变量
|_ 实例变量
|- 方法
|- 类方法
|- 实例方法
|_ 静态方法
|- 构造函数
|- 成员可见性
|_ 面向对象三大特性
|- 继承性
|- 封装性
|_ 多态性

构造函数

__init()__ 定义了一个构造函数。构造函数的意义在于初始化出不同(内容)的对象实例,不能使用类似方法调用一样传参数。

无参数的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student():
# 构造函数
def __init__(self):
print('student')

student1 = Student()
a = student1.__init__() # 不推荐显式调用构造函数

print(a)
### output
student
student
None

print(type(a))
### output
<type 'NoneType'>
  1. 当实例化时,类会自动调用__init__(),无需显式调用。也可以显式调用,但不推荐这样使用。
  2. 显式调用时,构造函数和普通函数一样,如果没有返回值,则返回 None
  3. 如果对构造函数设置返回值(例如return 'student'),只能返回 None,不能返回其他类型,否则会报错。

含参数的构造函数

通常构造函数用于初始化对象的特征,可通过构造函数传入参数,初始化属性值。

1
2
3
4
5
6
7
8
9
10
11
class Student():
name = ''
age = 0

def __init__(self, name, age):
# 构造函数,初始化对象的属性
name = name
age = age
print(name)

student = Student('name',18) # name

区分模块下的变量与类中的变量

模块中的全局变量和局部变量:

1
2
3
4
5
6
7
8
c = 50

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

add(1,2) # 3 局部变量
print(c) # 50 全局变量,全局变量不会因为局部变量的更改而更改

而类的机制与模块中全局变量和局部变量的机制完全不一样,类中赋值参考下例:

1
2
3
4
5
6
7
8
9
10
11
class Student():
name = ''
age = 0

def __init__(self, name, age):
# 构造函数,初始化对象的属性
name = name
age = age

student = Student('name',18)
print(student.name) # '' 并没有通过构造函数赋值成功

此处打印出空值的真正原因在于这里的nameage是实例变量。一定要注意「类变量」和「实例变量」的差别。

类变量与实例变量

实例变量由类模版创建而来,与对象相关联,与类没有关系。类变量与实例变量的区分方式如下:

  • 实例变量的定义方式:self.xxx (有self关键字)
  • 类变量的定义方式: xxx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():
name = 'aaa' # 类变量
age = 0 # 类变量

def __init__(self, name, age):
self.name = name # 实例变量赋值
age = age # 类变量赋值

# 实例
student1 = Student('student1',18)
student2 = Student('student2',20)

print(student1.name) # student1 实例变量
print(student2.name) # student2 实例变量
print(Student.name) # aaa 类变量

注:在类里,构造函数中的赋值并不会改变变量的值,但在模块中,函数内的赋值会改变全局变量的值。

实例变量的意义:
考虑将nameage设置为类变量是否合理,学生的姓名和年龄应与实例相关,因此推荐将其写为实例变量。举个例子,定义班级中所有学生的总数为sum变量,更适合作为类变量。

类变量的意义:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student():
# 班级中所有学生的总数
sum = 0 # 类变量

def __init__(self, name, age):
self.name = name # 实例变量赋值
age = age # 类变量赋值

student1 = Student('student1',18)
student2 = Student('student2',20)
print(student1.name) # student1
print(student2.name) # student2
print(Student.name) # aaa

类与实例变量查找顺序

对象下隐藏的内置变量__dict__,字典里保存着对象(类或者实例)下的所有变量。优先寻找实例变量,再寻找类变量;如果类中也找不到,则会到父类中寻找。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():
name = 'init' # 类变量
age = 0 # 类变量

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

student = Student('name',18)

print(student.name) # 'init' 实例变量
print(Student.name) # 'init' 类变量
print(student.__dict__) # {} 空字典,代表对象下没有变量
print(Student.__dict__) #
{'__module__': '__main__', 'name': 'init', 'age': 0, '__init__': <function Student.__init__ at 0x100837d90>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

student.__dict__打印出空字典,则代表着student实例下不存在任何变量,因此student.name找不到任何实例变量时,进一步寻找类变量,返回同名的类变量name

类方法与实例方法

实例方法与self

self 不是关键字,可以用任何名称,但 python 推荐使用 self

实例方法,参数列表的第一个必须为self(显式指定)。调用时不需要传入,默认传入。

self指的是调用的实例,与类无关。

1
2
3
4
5
6
7
8
9
10
11
class Student():
name = 'init'
age = 0

# 实例方法
def __init__(self, name, age):
self.name = name # 实例变量
self.age = age # 实例变量

student = Student('name',18)
print(student.__dict__) # {'name': 'name', 'age': 18}

通常情况下,方法用于操作/计算变量,以改变特征。

实例方法的一些例子

通过上面的学习,我们知道实例方法是可以操作实例变量的。
那么,实例方法内部可以访问类变量吗?

实例方法用于

构造函数的意义在于初始化类的各个特征和行为的。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student():
name = 'init'
age = 0

def __init__(self, name1, age):
self.name = name1 # 实例变量

print(self.name) # 'name' 实例属性
print(name) # 报错,'name' 读取的是形参name,内部无法访问类变量
print(age) # 报错 ,内部无法访问类变量

student = Student('name',18) # 调用类
print(student.name) # 调用实例

实例方法中访问类变量

只有在外部调用时可以,不能在内部调用时通过__dict__查找。

1. 外部访问

1
2
3
4
class Student():
name = 'init' # 类变量

Student.name # 'init' 外部访问类变量

2. 实例内部访问-方法一

1
2
3
4
5
6
7
class Student():
name = 'init' # 类变量

def __init__(self):
print(Student.name) # 'init' 内部访问类变量

student = Student()

3. 实例内部访问-方法二

可通过 self 自带的 __class__属性

1
2
3
4
5
6
7
class Student():
name = 'init' # 类变量

def __init__(self):
print(self.__class__.name) # 'init' 内部访问类变量

student = Student()

举个例子,下例中实现了每创建一个实例,类变量sum就加1。

1
2
3
4
5
6
7
8
9
10
class Student():
sum = 0 # 类变量

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

student1 = Student() # 当前班级学生总数为:1
student2 = Student() # 当前班级学生总数为:2
student3 = Student() # 当前班级学生总数为:3

类方法

实例方法通常用来操作实例变量,类方法通常用于操作类变量。从上例中可以看到 __init__()中操作了类方法,那么将其封装为一个方法,即为类方法。

类方法具有一个默认的关键字cls(推荐使用)和@classmethod装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student():
sum = 0 # 类变量

# 类方法
@classmethod
def plus_sum(cls):
cls.sum += 1
print(cls.sum)

# 类方法的调用
student1 = Student()
Student.plus_sum() # 1
student2 = Student()
Student.plus_sum() # 2
student3 = Student()
Student.plus_sum() # 3

类方法和实例方法的区别:

  • 实例方法关联的是实例,而类方法关联的是类本身
  • 尽管实例方法也可以操作类变量,但操作与对象无关的变量时,推荐使用类方法
  • cls 代表调用类方法的类,上例中为Student

实例对象调用类方法

实例对象可以调用类方法,但不推荐这样使用。

1
2
3
4
5
6
7
8
9
10
11
12
class Student():
sum = 0 # 类变量

# 类方法
@classmethod
def plus_sum(cls):
cls.sum += 1
print(cls.sum)

# 类方法的调用
student1 = Student()
student1.plus_sum() # 1 实例对象可以调用类方法

静态方法

静态方法必须具有装饰器 @staticmethod。类和实例对象均可调用静态方法。静态方法类似于类方法,但不需要传入任何指定参数。静态方法均可以用类方法代替,不推荐经常使用

静态方法和类方法的共同点在于:

  • 类方法和静态方法均无法访问实例变量

静态方法和类方法的区别在于:

  • 静态方法不需要显式传入cls
  • 静态方法内部可以访问类变量,由于没有cls,必须使用类全称调用类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Student():
sum = 0 # 类变量

# 静态方法
@staticmethod
def add(x,y):
print(Student.sum) # 静态方法访问类变量
print(self.sum) # 静态方法访问实例变量,会报错
print('This is a static method')


# 类方法的调用
student1 = Student()
student1.add(1,2) # 实例调用静态方法
Student.add(1,2) # 类调用静态方法