lesson 040 —— 面向对象简介
关于面向对象的简单介绍。
三大编程范式
- 面向过程编程
- 函数式编程
- 面向对象编程
面向对象概念
- 类: 把一类事物相同的特征和动作整合到一起就是类。类是一个抽象的概念。类是一种数据结构。
- 对象:就是基于类而创建的一个具体的事物(具体存在的),也是特征和动作整合到一起;对象就是类的实例化,具体化。对象是一个具体的事物。
- 类与对象的关系: 对象是由类产生的,对象是类的实例化,具体化。
- 实例化: 由类产生对象的过程叫实例化,类实例化的结果就是一个对象,或者叫一个实例(实例=对象)。
1. 声明类
class 类名:
"""类的文档字符串"""
类体
# 声明一个类,类名一般首字母大写,使用驼峰命名法
# 代表的是:class Data(object):
class Data:
"""这是一个时间的类。"""
pass
# 实例化这个类
d1 = Data()
在 Python3 中使用新式类,所有的类都继承自一个父类:object。
2. 属性
类是用来描述一类事物的,类的对象指的是一类事物中的一个个体。事物的属性分为:
- 数据属性: 就是变量
- 函数(方法)属性: 就是函数,在面向对象里称为方法。
注意:类和对象都使用点 .
来访问自己的属性。
3. 类的属性
类的数据属性: 类的数据属性是所有对象共享的
类的方法属性: 类的函数属性是绑定给对象用的
-
一些内置的类的特殊属性
类名.__name__# 类的名字(字符串) 类名.__doc__# 类的文档(字符串) 类名.__base__# 类的第一个父类(在讲继承时会讲) 类名.__bases__# 类所有父类构成的元组(在讲继承时会讲) 类名.__dict__# 类的字典属性 类名.__module__# 类定义所在的模块 类名.__class__# 实例对应的类(仅新式类中)
使用 dir(类名) 可以查看类的属性!!!
class Data:
"""关于时间的类"""
# 类的数据属性
year = 2018
month = 7
day = 35
week = '星期三'
# 类的方法属性
def print_year():
print('2018 年')
# 需要传入参数和这个类绑定,不传也没问题,可是不会绑定
# 可是要使用参数的时候,就需要传入 self
def print_data(self):
print('%4d 年 %2d 月 %2d 日' % (self.year, self.month, self.day))
if __name__ == '__main__':
d1 = Data()
d2 = Data()
print("类的属性")
print("Data.year: %d" % Data.year)
print("Data.print_year: ")
Data.print_year()
# 这里类调用的话,就需要传入一个参数
print("Data.print_data: ")
Data.print_data(Data)
print()
print("类的属性是所有对象公有的")
print("id Data.year: %d" % id(Data.year))
print("id d1.year: %d" % id(d1.year))
print("id d2.year: %d" % id(d2.year))
print()
print("函数属性是对于不同对象的")
print("addr Data.print_data: ", Data.print_data)
print("addr d1.print_data: ", d1.print_data)
print("addr d2.print_data: ", d2.print_data)
print("id Data.print_data: %d" % id(Data.print_data))
print("id d1.print_data: %d" % id(d1.print_data))
print("id d2.print_data: %d" % id(d2.print_data))
print()
print("d1 对象与类属性关系")
print("d1.year: %d" % d1.year)
# 这里不能调用,因为类里面是没有参数的,对象调用的话就会传入对象,即 self 参数
#print("d1.print_year: ")
#d1.print_year()
print("d1.print_data: ")
d1.print_data()
print()
print("类改变数据属性:")
print("Data.year: %d" % Data.year)
print("d1.year: %d" % d1.year)
print("d2.year: %d" % d2.year)
print("执行 Data.year = Data.year + 1")
Data.year = Data.year + 1
print("Data.year: %d" % Data.year)
print("d1.year: %d" % d1.year)
print("d2.year: %d" % d2.year)
print()
print("对象改变数据属性:")
print("Data.year: %d" % Data.year)
print("d1.year: %d" % d1.year)
print("d2.year: %d" % d2.year)
print("执行 d1.year = d1.year + 1")
d1.year = d1.year + 1
print("Data.year: %d" % Data.year)
print("d1.year: %d" % d1.year)
print("d2.year: %d" % d2.year)
print("执行 d2.year = d2.year + 1")
d2.year = d2.year + 1
print("Data.year: %d" % Data.year)
print("d1.year: %d" % d1.year)
print("d2.year: %d" % d2.year)
print()
print("改变之后的地址")
print("id Data.year: %d" % id(Data.year))
print("id d1.year: %d" % id(d1.year))
print("id d2.year: %d" % id(d2.year))
print()
结果:
类的属性
Data.year: 2018
Data.print_year:
2018 年
Data.print_data:
2018 年 7 月 35 日
类的属性是所有对象公有的
id Data.year: 139849464558928
id d1.year: 139849464558928
id d2.year: 139849464558928
函数属性是对于不同对象的
addr Data.print_data:
addr d1.print_data: >
addr d2.print_data: >
id Data.print_data: 139849464102288
id d1.print_data: 139849465338376
id d2.print_data: 139849465338376
d1 对象与类属性关系
d1.year: 2018
d1.print_data:
2018 年 7 月 35 日
类改变数据属性:
Data.year: 2018
d1.year: 2018
d2.year: 2018
执行 Data.year = Data.year + 1
Data.year: 2019
d1.year: 2019
d2.year: 2019
对象改变数据属性:
Data.year: 2019
d1.year: 2019
d2.year: 2019
执行 d1.year = d1.year + 1
Data.year: 2019
d1.year: 2020
d2.year: 2019
执行 d2.year = d2.year + 1
Data.year: 2019
d1.year: 2020
d2.year: 2020
改变之后的地址
id Data.year: 139849465104656
id d1.year: 139849464558832
id d2.year: 139849464558896
从以上实例中我们可以验证得到这几个结论:
-
类的数据属性是所有对象共享的,我们称为类的数据
id Data.year: 139849464558928 id d1.year: 139849464558928 id d2.year: 139849464558928
-
当使用类改变类的数据之后,我们发现,所有的对象数据都改变了,即他们的地址都是一样的。
类改变数据属性: Data.year: 2018 d1.year: 2018 d2.year: 2018 执行 Data.year = Data.year + 1 Data.year: 2019 d1.year: 2019 d2.year: 2019
-
但是,我们使用对象改变类的属性之后,仅仅是这个对象的数据变了,而其它对象和类的都不会改变。我们可以得知:Python 中,直接改变对象中类的数据,其实是创建了新的对象数据,而不是改变了类的数据,即将指向类的数据的变量指向了一个新的数据地址!!!
对象改变数据属性: Data.year: 2019 d1.year: 2019 d2.year: 2019 执行 d1.year = d1.year + 1 Data.year: 2019 d1.year: 2020 d2.year: 2019 执行 d2.year = d2.year + 1 Data.year: 2019 d1.year: 2020 d2.year: 2020 id Data.year: 139849465104656 id d1.year: 139849464558832 id d2.year: 139849464558896
注意:Python 中几乎所有变量的赋值,都是将新的数据地址赋值给这个变量,即这个变量指向了一个新的内存地址,而不是改变变量指向的对应内存地址的数据!!!
在 obj.name 会先从 obj 自己的名称空间里找 name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常.
-
类中的函数属性需要一个 self 形参来和这个类的对象绑定。在 Python 中,self 代表的就是对象,定义的函数属性本质上是定义一个函数。(虽然不加 self 程序不会报错,但是不会和对象绑定)。虽然绑定了,但是当类调用的时候可以随便传入参数,只要传入的参数使函数可以运行。
def print_data(self): print('%4d 年 %2d 月 %2d 日' % (self.year, self.month, self.day)) Data.print_data(Data)
-
类的函数属性没有 self 参数的时候,只有类可以调用,对象是无法调用的,它是类的方法。
# 这里不能调用,因为类里面是没有参数的,对象调用的话就会传入对象,即 self 参数 #print("d1.print_year: ") #d1.print_year()
-
通过类来创建对象的时候,对于绑定的类的函数,即有 self 形参的函数,实际上是将那个函数复制到了对象的空间中,有了自己的内存,与类中的地址不一样。所以,通过类创建对象,虽然是同一个类,同一个类的函数,但是,每个对象都会复制函数,每个对象有他们自己的存储这个函数的内存,即变成了对象自己的函数,而不是类的函数。虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法!!!
addr Data.print_data:
addr d1.print_data: > addr d2.print_data: > id Data.print_data: 139849464102288 id d1.print_data: 139849465338376 id d2.print_data: 139849465338376 -
强调:绑定到对象的方法的特殊之处在于,绑定给谁就由谁来调用,谁来调用,就会将谁本身当做第一个参数传给方法,即自动传值(方法
__init__
也是一样的道理)注意:绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数 self,self 可以是任意名字,但是约定俗成地写出self。
# 类调用 Data.print_data(Data) # 对象调用 d1.print_data()
4. 对象
对象是类的实例化,类实例化的一个结果称为一个实例或者一个对象。
class Student:
school='xxoo'
def __init__(self, name, sex, age):
print('开始创建')
self.name = name
self.age = age
self.sex = sex
print('创建结束')
def learn(self):
print('%s is learning' % self.name) #新增self.name
def eat(self):
print('%s is eating' % self.name)
def sleep(self):
print('%s is sleeping' % self.name)
# 创建对象
s1=Student('李坦克','男',18)
s2=Student('王大炮','女',38)
s3=Student('牛榴弹','男',78)
对象不仅有函数属性,还有类的数据属性,另外,还有它自己的数据属性。它们都可以通过 .
来访问。
对象的 __dict__
属性只有函数属性,说明了对象只有数据属性。
要使用 __init__
来设置初始化一个对象的方法,它没有返回值。
对于每一个类里定义的参数,都必须要有 self 形参,且必须要有,还要放在第一个位置。
5. 类属性增删改查
类属性的操作使用 .
与 del
进行操作,当使用类进行类属性的更改,它的所有的实例对象,不管是改变之前创建的,或者是改变之后创建的,都会跟着改变;当使用对象对类属性重新赋值之后,类属性实际不会改变,而这个对象的类属性转变为对象属性。数据属性与函数属性都一样,同样的方式。
注意: 要调用类的属性,只能通过类名加点或者对象加点的方式调用,如果直接使用变量名,则调用的是全局的变量。调用不到类的属性。
country = '中国'
class N:
country = '日本'
num = ['a', 'b',]
def __init__(self):
print(country) # 输出的是中国
n1 = N()
print(N.num)
print(n1.num)
n1.num.append(1) # 没有给类属性赋新值,只是改变了类属性,所以结果是类属性改变了
print(N.num)
print(n1.num)
结果:
中国
['a', 'b']
['a', 'b']
['a', 'b', 1]
['a', 'b', 1]