假设我们要处理学生的成绩表,为了表示一个学生的成绩:
面向过程的方法:用一个dict表示,在进行函数调用
std1 = { 'name': 'Michael', 'score': 98 }
std2 = { 'name': 'Bob', 'score': 81 }
#而处理学生成绩可以通过函数实现,比如打印学生的成绩:
def print_score(std):
print('%s: %s' % (std['name'], std['score']))
print_score(std2)
print_score(std1)
Bob: 81
Michael: 98
面向对象的方法:创建出这个学生对应的对象,给对象发一个print_score消息,让对象自己把自己的数据打印出来。
class Student(object):
# 定义初始化方法,创建实例对象自动调用,对属性赋值
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
hongenjie = Student('洪恩节',100)
lisa = Student('Lisa Simpson', 87)
hongenjie.print_score()
lisa.print_score()
洪恩节: 100
Lisa Simpson: 87
如果采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,
而是Student这种数据类型应该被视为一个对象,这个对象拥有name和score这两个属性。
如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,
给对象发一个print_score消息,让对象自己把自己的数据打印出来,给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法
面向对象的设计思想是抽象出Class,根据类创建实例,面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法
经典类:class Cat:
# 定义一个猫类
class Cat:
# 定义猫的属性(color,legs)
color = '黄色'
legs = 4
# 定义猫的方法(eat,sleep)
def eat(self):
print('猫吃鱼')
def sleep(self):
print('猫睡觉')
my_cat=Cat()#创建实例my_cat
my_cat.eat()#访问方法
my_cat.sleep()#访问方法
print(Cat.color)#访问属性
print(Cat.legs)#访问属性
猫吃鱼
猫睡觉
黄色
4
新式类:class Car(object):
# 定义一个车类
class Car(object):
# 属性:颜色,四个轮子
color = '白色'
wheel = 4
# 行为:跑,鸣笛
def move(self):
print('车子在移动')
def whistle(self):
print('车子在鸣笛')
my_car=Car()
my_car.move()
my_car.whistle()
print(Car.color)
print(Car.wheel)
车子在移动
车子在鸣笛
白色
4
当创建实例对象的时候,对象自动拥有方法,对象就可以直接调用方法(属性也一样)。
属性的添加语法格式:对象名.属性名 = 新的值
属性的获取语法格式:print(对象名.属性名)
对象调用的方法语法格式:对象名.方法名()
#定义一个关于person的类
class Person(object):#属性:身高,体重,年龄#行为:吃饭 睡觉 喝水
#定义普通的方法
def eat(self):
print('人都要吃饭')
def sleep(self):
print('人都要休息')
def drink(self):
print('水是生命之源')
#通过Person创建一个实例对象p
p = Person()
#直接添加属性
p.name = '张三'
p.age = 20
p.height=180
p.weight=60
#获取属性
print(p.name)
print(p.age)
print(p.height)
print(p.weight)
#对象调用方法
p.eat()
p.sleep()
p.drink()
张三
20
180
60
人都要吃饭
人都要休息
水是生命之源
不仅如此,我们还可以创建新的对象p2,它可以和p有不同的属性,但他们含有相同的方法。
#通过Person创建一个对象p2
p2 = Person()
#直接添加属性
p2.name = '李四'
p2.age = 30
p2.sex = '男'
#获取属性
print(p2.name,p2.age,p2.sex)
#对象调用方法
p2.eat()
p2.sleep()
p2.drink()
李四 30 男
人都要吃饭
人都要休息
水是生命之源
类中的函数称为方法,我们在python前面的基础学习中,学到的任何函数都适用于方法,唯一的区别在于调用方法的方式不同。
init(self,属性1,属性2)是一个特殊的方法,每当我们所创建的类产生新的示例的时候,python都会自动运行它,在这个运行方法的名称中,开头和末尾均有两个下划线,这是避免与普通方法名称产生矛盾,因此必须确保__init__(self,属性1,属性2)的两边都有两个下划线,否则当你使用类来创建示例时,将不会自动调用这个方法,进而引发难以发现的错误。
举例:
#定义一个汽车类
class Car(object):
def __init__(self,color,wheelNum):
self.color = color
self.wheelNum = wheelNum
# 定义一个普通方法
def move(self):
print('车子在移动')
# 定义一个普通方法
def whistle(self):
print('车子在鸣笛')
#创建一个对象bmw
bmw = Car('红色',4)
#调用类里面普通方法
bmw.move()
bmw.whistle()
print(bmw.color)
print(bmw.wheelNum)
细心的小伙伴可能都发现了为什么__init_()属性里面包含了self.
为何必须在方法定义中包含形参self呢?
因为python调用这个方法创建示例时,将自动传入实参self,每个与示例相关联的方法调用都自动传递实参self,它是一个指向示例本身的引用,让示例能够访问类中的属性和方法。
属性我们可以在编写__init__()的时候将属性的值指定,也可以是通过实参来传递属性的值。
指定属性的值:
def __init__(self,color,wheelNum):
self.color = '黑色'
self.wheelNum = 4
不指定属性的值:
def __init__(self,color,wheelNum):
self.color = color
self.wheelNum = wheelNum
那么这两种有什么区别?
区别在于如果指定了属性的值,那么在调用属性的时候,即使实参传递了和指定值不相同的,输出结果依然为指定值。
举例:
def __init__(self,color,wheelNum):
self.color = '黑色'
self.wheelNum = 4
bmw = Car('红色',4)
print(bmw.color)
print(bmw.wheelNum)
黑色
4
难道属性的值就不能被改变吗?当然不是
下面我们对上面例子中的属性进行修改:
bmw.color="yellow"
bmw.wheelNum=10
直接修改 对象名.属性名 = 新值
完整代码:
class Car(object):
def __init__(self,color,wheelNum):
self.color = '黑色'
self.wheelNum = 4
def move(self):
print('车子在移动')
def whistle(self):
print('车子在鸣笛')
bmw = Car('红色',4)
bmw.move()
bmw.whistle()
bmw.color="yellow"
bmw.wheelNum=10
print(bmw.color)
print(bmw.wheelNum)
这次的颜色和车轮数很好的被修改了
车子在移动
车子在鸣笛
yellow
10
当实例化类得到具体对象的时候,会自动调用__init__()方法,对类的属性进行初始化赋值,创建对象的时候,自动拥有类里面属性
import time
#定义一个动物类
class Animal(object):
#定义初始化方法 创建对象时候自动调用
def __init__(self,name):
print('__init__方法被调用')
self.name = name
#定义普通方法
def walk(self):
print('动物会跑')
#定义一个析构方法 删除对象的时候自动调用
def __del__(self):
print('__del__方法被调用')
print('%s对象被干掉'%(self.name))
#创建一个dog对象
dog = Animal('哈皮狗')
dog.walk()
__init__方法被调用
动物会跑
__del__方法被调用
哈皮狗对象被干掉
通过输出结果我们发现,为什么__del__()也被调用了呢?
原因是:析构函数__del__(),使用del删除实例时才触发,否则等到文件执行完毕进行回收时才触发。
修改之后:
import time
# 定义一个动物类
class Animal(object):
#定义初始化方法 创建对象时候自动调用
def __init__(self,name):
print('__init__方法被调用')
self.name = name
#定义普通方法
def walk(self):
print('动物会跑')
#定义一个析构方法 删除对象的时候自动调用
def __del__(self):
print('__del__方法被调用')
print('%s对象被干掉'%(self.name))
#创建一个dog对象
dog = Animal('哈皮狗')
dog.walk()
# 手动销毁对象,再测试对象调用类中方法报错
del dog
dog.walk()
此时编译器告诉我们dog没有被定义,事实上是因为使用del将其删除了
上面代码中,dog只被引用了一次,因此使用一次del,该对象就被真正的删除了,那么如果是下面这种情况呢?
cat被引用了3次,如果我们去用和面一样的方法,会成功实现对象的删除吗?
cat=Animal("波斯猫")
cat2=cat
cat3=cat
删除cat,去调用cat2和cat3
class Animal(object):
def __init__(self,name):
print('__init__方法被调用')
self.name = name
def walk(self):
print('动物会跑')
def __del__(self):
print('__del__方法被调用')
print('%s对象被干掉'%(self.name))
cat=Animal("波斯猫")
cat2=cat
cat3=cat
del cat
cat3.walk()
cat2.walk()
__init__方法被调用
动物会跑
动物会跑
__del__方法被调用
波斯猫对象被干掉
cat2和cat3成功被调用了,难道是对象没被删除?下面我们调用一下cat.
class Animal(object):
def __init__(self,name):
print('__init__方法被调用')
self.name = name
def walk(self):
print('动物会跑')
def __del__(self):
print('__del__方法被调用')
print('%s对象被干掉'%(self.name))
cat=Animal("波斯猫")
cat2=cat
cat3=cat
del cat
cat.walk()
Traceback (most recent call last):
File "C:/Users/Lenovo/PycharmProjects/pythonProject4/main.py", line 295, in <module>
cat.walk()
NameError: name 'cat' is not defined
__init__方法被调用
__del__方法被调用
波斯猫对象被干掉
通过结果我们可得出如下结论:
在本例中,cat被删除,但不影响cat2/cat3的调用,其他情况亦是如此。
变量保存了对象的引用,对象的引用计数加1,使用__del__()删除对象的时候,对象的引用计数-1,对象被删除,如果对象的引用计数不是1,每删除一次对象的引用计数减1,直到引用计数为0时候,该对象才被真正的删除