十二 面向对象
1、面向对象简介
▶ Python是一门面向对象的编程语言
▶ 所谓面向对象的语言,简单理解就是语言中的所有操作都是通过对象来进行的
▶ 面向过程:
● 面向对象过程指我们将程序分解为一个一个步骤,通过每个步骤的抽象来完成程序
● 这种编写方式往往只适用于一个功能,如果要实现别的功能,往往复用性比较低
● 这种编写方式符合人类的思维,编写比较容易
1.妈妈穿衣服穿鞋出门
2.妈妈骑上电动车
3.妈妈到超市门口放好电动车
4.妈妈买西瓜
5.妈妈结账
6.妈妈骑电动车回家
7.到家孩子吃西瓜
● 面向对象的编程语言,关注的是对象,而不注重过程,对于面向对象一切皆对象
● 以上方式可以用妈妈给孩子买瓜来解决
● 面向对象比较容易阅读,易于维护,复用性好,但是编写起来比较麻烦
2、类的简介
▶ 类(class):简单理解类就是一张图纸,在程序中我们需要根据类型来创建对象,我们也称对象是类的实例(instance),如果多个对象是通过一个类创建的,我们称这些对象是一类对象。
#a=1 int() str() bool list()......
a=int(1) #创建一个int类的实例
b=str('python')
print(a,type(a))
print(b,type(b))
#输出:1 <class 'int' >
python <class 'str'>
▶自定义的类,类的名字要使用大写字母开头,使用大驼峰命名法
class MyClass():
pass
print(MyClass)
输出:<class '_main_.MyClass'>
class MyClass():
pass
#mc就是通过MyClass创建的对象,mc是MyClass的实例
mc=MyClass()
mc_2=MyClass()
mc_3=MyClass()
mc_4=MyClass()
#mc mc_2 mc_3 mc_4都是MyClass的实例
▶ 类也是一个对象,用来创建对象的对象,类是type类型的对象,定义类实际上就是定义一个type类型的对象
#print(id(MyClass),type(MyClass))
▶ 我们可以通过对象添加变量,对象添加变量我们称之位属性
▶ 语法:对象.属性名=属性值
mc.name='蜘蛛侠'
print(mc_2.name)
▶ 类的定义:类和对象都是现实生活中事物的抽象
▶ 实际上所有的事物都有两部分构成
1.数据(属性)
2.行为(方法)
#尝试定义一个人类
class Person():
#在类的代码块中,我们可以定义变量和函数
#在类中我们定义的变量,将会成为所有实例的公共属性
#所有实例都可以访问这些变量
#a=10
#b=20
name='蜘蛛侠'
#在类中也可以定义函数,类中定义的函数我们称之位方法
#这些方法可以通过该类的实例来访问
def speak(a):
print('hello!')
p1=person()
p2=person()
#print(p1.a)
#print(p2.b)
#调用方法 对象.方法名
#方法调用和函数调用的区别
#如果函数调用,调用的时候有几个形参,就会传递几个实参
#但是如果是方法调用,默认传递一个参数,方法中至少定义一个形参
p1.speak() #TypeError:没有形参,在def speak(a),加上参数a
p1.speak()
#输出:hello!
#为什么实例对象能够访问到类对象中的属性和方法呢?
#类中定义的属性和方法都是公共的,任何该类的实例都可以访问
▶ 属性和方法的查找流程:
1.当我们调用一个对象的属性时,解析器会在当前对象中寻找是否含有该属性
2.如果有,则直接返回当前对象,如果没有呢,则去当前对象的类对象中去寻找,如果类对象中有则返回对象中的属性值,如果没有,则报错
class Person:
name='蜘蛛侠'
def speak(a):
print('你好我是%s'%.name) #NameError:name 'name' is not defined
p1=Person()
p2=Person()
p1.name='钢铁侠'
p2.name='緑巨人'
p1.speak()
p2.speak()
class Person:
name='蜘蛛侠'
def speak(a):
print('你好我是%s'%.p1.name)
p1=Person()
p2=Person()
p1.name='钢铁侠'
p2.name='緑巨人'
p1.speak()
p2.speak()
输出:你好我是钢铁侠
你好我是钢铁侠
class Person:
name='蜘蛛侠'
def speak(a):
print('你好我是%s'%.a。name)
p1=Person()
p2=Person()
p1.name='钢铁侠'
p2.name='緑巨人'
p1.speak()
p2.speak()
#输出:你好我是钢铁侠
你好我是緑巨人
▶ 方法每次调用时,解析器都会自动传递一个参数
▶ 第一个参数,就是调用方法的本身
▶ 如果p1调用,第一参数就p1对象
▶ 如果p2调用,第一个参数就是p2对象
▶ a可以是任意参数,一般习惯把这个参数命名为self
3、类的特殊方法
▶例子:
class Person:
name='葫芦娃'
def speak(self):
print('大家好,我是%s'$self.name)
p1=Person()
p2=Person()
p.1speak()
p.2speak()
输出:大家好,我是葫芦娃
大家好,我是葫芦娃
class Person:
#name='葫芦娃'
def speak(self):
print('大家好,我是%s'$self.name)
p1=Person()
#手动添加属性
p1.name='钢铁侠'
p2=Person()
p2.name='蜘蛛侠'
p.1speak()
p.2speak()
输出:大家好,我是钢铁侠
大家好,我是蜘蛛侠
#通过观察我们发现了一些问题
#1.对于Person类来说,name是必须的
#2.对于这个name属性又是不同的
#我们希望在创建对象时,必须设置name属性,如果不设置对象无法创建
class Person:
#在类中可以用特殊方法,如__开头和__结尾
#特殊方法不需要我们自己调用
def __init__(self):
print('hello')
def speak(self):
print('大家好,我是%s'$self.name)
p1=Person()
p1.__init__()
输出:hello
hello
class Person:
#在类中可以用特殊方法,如__开头和__结尾
#特殊方法不需要我们自己调用
def __init__(self):
print('hello')
def speak(self):
print('大家好,我是%s'$self.name)
p1=Person()
#p1.__init__()
输出:hello
class Person:
#在类中可以用特殊方法,如__开头和__结尾
#特殊方法不需要我们自己调用
def __init__(self,name):
self.name=name
def speak(self):
print('大家好,我是%s'%self.name)
p1=Person('蜘蛛侠')
输出:蜘蛛侠
class Person:
#在类中可以用特殊方法,如__开头和__结尾
#特殊方法不需要我们自己调用
def __init__(self,name):
self.name=name
def speak(self):
print('大家好,我是%s'%self.name)
p1=Person('蜘蛛侠')
p1.speak()
输出:大家好,我是蜘蛛侠
▶ 创建流程:
p1=Person()
1.创建一个变量
2.在内存中创建一个新的对象
3.执行类中的代码块(只执行一次)
4.__init__(self)方法执行
▶ 类的基本结构
'''
class 类名([副类 ]):
公共属性...
#对象的初始化方法
def __init__(self,......): #这里前后是双下滑线
代码块
#其它方法
def m1(self,......):
代码块
def m2(self,......):
代码块
......
'''
4、封装的引入
▶ 出现封装的原因:我们需要一种方式来增强数据的安全性。
1.属性不能随意修改
2.属性不能改为任意值
▶封装是面向对象的三大特征之一
▶封装是指隐藏对象中一些不希望被外部所访问的属性或方法
例子:
class Dog:
def__init__(self,name):
self.name=name
d=Dog('德牧')
输出:德牧
d.name='二哈' #对name进行了修改
输出;二哈
class Dog:
def__init__(self,name):
self.hidden_name=name
def speak(self):
peint('大家好,我是%s'%self.hidden_name)
d=Dog('德牧')
d.name='二哈' #将对象属性名改成一个外部不知道的名字
d.speak()
输出:大家好,我是德牧
▶ 我们也可以提供给一个getter()和setter()方法是外部可以访问到属性
1.getter()获取对象中指定的属性
2.setter()用来设置对象指定的属性
class Dog:
def__init__(self,name):
self.hidden_name=name
def get_name(self):
return self.hidden_name
def set_name(self,name)
self.hidden_name
d=Dog('德牧')
▶ 使用封装,确实增加了类的定义的复杂程度 但是它也确保了数据的安全
1.隐藏属性名,使调用者无法随意修改对象中的属性。
2.增加了getter()和setter()方法,很好控制属性是否是只读的
3.使用setter()设置属性,可以增加数据的验证
4.使用getter()方法获取属性,使用setter()方法设置属性,可以在读取属性和修改属性的同时做一些其他的处理。
▶可以为对象的属性使用双下滑线开头__xxx。双下滑线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
class Person:
def __init__(self,name):
self.hidden_name=name
def get_name(self):
return self.hidden_name
def set_name(self,name)
self.hidden_name=name
p=Person('蜘蛛侠')
#输出:蜘蛛侠
p.set_name('钢铁侠')
#输出:钢铁侠
p.hidden_name='緑巨人'
#输出:緑巨人
▶ 其实隐藏属性只不过是Python自动为属性修改了一个名字—>__name–>_Person__name
▶一般你若要告诉别人这个属性是封装的,以_开头
class Person:
def __init__(self,name):
self.__name=name
def get_name(self):
return self.__name
def set_name(self,name)
self.__name=name
p=Person('蜘蛛侠')
print(__name)
#输出:错误,找不到name
p.__name='緑巨人'
print(p.get_name())
#输出:蜘蛛侠
p._Person__name='緑巨人'
print(p.get_name())
#输出:緑巨人
▶一般情况下,使用_开头的属性都是私有属性,没有特殊条件情况下不要修改私有属性
5、property装饰器
▶ 我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性背修改。
例子:
class Person:
def __init__(self,name):
self._name=name
def name(self):
#这是一个getter()的方法
return self._name
p=Person('葫芦娃')
print(p.name())
输出:葫芦娃
class Person:
def __init__(self,name):
self._name=name
@property
def name(self):
#这是一个getter()的方法
return self._name
p=Person('葫芦娃')
print(p.name())
输出:TypeError::'str ' object is not callable
class Person:
def __init__(self,name):
self._name=name
@property
def name(self):
#这是一个getter()的方法
print('get方法执行了')
return self._name
p=Person('葫芦娃')
p.name='钢铁侠'
print(p.name)
输出:get方法执行了
葫芦娃
class Person:
def __init__(self,name):
self._name=name
@property
def name(self):
#这是一个getter()的方法
print('get方法执行了')
return self._name
@name.setter
def set_name(self,name):
self._name=name
p=Person('葫芦娃')
p.name='钢铁侠'
print(p.name)
输出:get方法执行了
钢铁侠