一、面向对象(OOP)的简介
什么是对象?
对象就是内存中存储指定数据的一块区域,实际上对象就是一个容器,专门用来存储数据。
二、对象的结构
一个对象我们会保存三组数据:id,type,value。
id(标识):用来标识对象的唯一性,每个对象都有唯一的id,id是由解释器生成的,id就是对象的内存地址,通过id()函数来查看对象的内存地址。
type(类型):类型就决定了对象有哪些功能,python是一种强类型语言,通过type()函数来查看对象的类型。
value(值):值指的就是对象中存储的具体数据。
对象分为两大类:可变对象(例如,list,set,dict)和不可变对象(例如,int,float,string,tuple,bool)。
三、面向对象的举例
所谓的面向对象简单理解就是语言中所有的操作都是通过对象来进行的。
举例:
把大象装冰箱共分为几步?(面向过程)
第一步:打开冰箱门;
第二步:装大象;
第三步:关门
面向对象:打开功能,存储功能,关闭功能
面向过程 (代表语言C)—>–>> 面向对象
定义冰箱这么一个对象:
第一、面向对象是一种思考问题的方式,面向对象它是一种思想;
第二、面向对象将实物变得简单化了。
人由执行者变为指挥者,面向对象具有继承,封装和多态
想要实现一个功能,先去找一个对象,维护对象,建立对象间的关系。
孩子吃瓜
第一步:妈妈穿衣服穿鞋出门;
第二步:妈妈骑电动车;
第三步:妈妈到超市门口停好电动车;
第四步:妈妈买瓜;
第五步:妈妈结账;
第六步:妈妈骑电动车回家;
第七步:到家孩子吃瓜;
面向过程是将一个动能分解成一个一个小的步骤,但是这种编程方式往往只适用于一个功能,我们要实现其他功能的时候,往往需要编写新的代码,复用性比较低,优点在于它比较符合人的思维,编写起来比较容易。
面向对象就是让 孩子吃瓜
面向对象的好处容易维护,方便复用,但是编写起来比较麻烦。
四、类(class)的简介
目前学习的对象都是python内置的对象(比如int、bool、str、list、dict等);python内置的类都是小写开头的,我们自己定义的类名要以大写开始以便于区分。
类简单理解就是一张图纸,在程序中我们需要根据类来创建对象。
定义一个类:
语法:class 类名(【父类】):
代码块
class MyClass():
pass
print(MyClass) #<class '__main__.MyClass'>
mc=MyClass() #用MyClass这个类创建了一个对象mc,mc也是MyClass的实例。
print(mc,type(mc))#<__main__.MyClass object at 0x00000234861FE4C8> <class '__main__.MyClass'>
#isinstance()用来检查一个对象是否是一个类的实例。
result=isinstance(mc,MyClass)
print(result) #True
五、对象的创建流程
类是什么?
类也是一个对象,类是一个可以创建对象的对象,对象包含id/type/value。
class MyClass():
pass
mc=MyClass()
#现在通过MyClass创建的类都是空类
#可以向对象中添加变量,对象中的变量我们称之为属性;
#语法:对象。属性=属性值
mc.name="金刚"
#类是一个type类型的对象
print(mc.name,type(MyClass),type(mc))#金刚 <class 'type'> <class '__main__.MyClass'>
六、类的定义
实际上所有的事务就是两部分组成的:
(1)数据(属性)
(2)行为(方法)
#定义一个人类
class Person():
#在类的代码块中可以定义变量和函数
#在类中的变量称为类属性,将会成为所有实例的公共属性,所有实例都可以访问这些变量
# a=10
# b=20
name="金刚葫芦娃"
#在类中的函数称之为方法;
#这些方法可以在实例中访问
#如果是函数(函数本身)调用,有几个形参就传几个形参
#如果是方法(类.函数),默认传递一个参数,所以类中的方法至少要定义一个形参。
def speak(self):
print("神仙")
pass
#创建Person的实例
p1=Person()
p2=Person()
# print(p1.a,p2.b) #10 20
print(p1.name,p2.name) #金刚 金刚
p1.speak() #神仙
七、属性和方法
属性和方法的查找流程:
当我们调用一个属性和方法的时候,解析器会先在当前的对象当中寻找是否有该属性或者方法,如果有,则返回当前对象的属性值;
如果没有,则去当前对象中的类对象去寻找,如果有则返回类对象中的属性值,如果类对象中也没有,则报错。
#定义一个人类
class Person():
#在类的代码块中可以定义变量和函数
#在类中的变量称为类属性,将会成为所有实例的公共属性,所有实例都可以访问这些变量
# a=10
# b=20
name="金刚葫芦娃"
#在类中的函数称之为方法;
#这些方法可以在实例中访问
#如果是函数(函数本身)调用,有几个形参就传几个形参
#如果是方法(类.函数),默认传递一个参数,所以类中的方法至少要定义一个形参。
def speak(self,name):
#传递的这个参数,就是调用方法的对象本身,实例化自身,id一样
print(self)
print("你好我是%s"%name)
pass
#创建Person的实例
p1=Person()
p2=Person()
p1.name="钢铁侠"
p2.name="绿巨人"
# print(p1.a,p2.b) #10 20
# print(p1.name,p2.name) #金刚 金刚
print(p1)
p1.speak(p1.name) #神仙
p2.speak(p2.name) #神仙
class A(object):
#类属性:直接在类中定义的属性就是类属性
#类属性可以通过类或类的实例访问到
#类属性只能通过类对象来修改,无法通过实例对象来修改
count=0
def __init__(self):
#这个是实例属性,通过实例对象添加的属性属于实例属性
#实例属性只能通过实例属性来访问和修改,类对象无法访问和修改。
self.name='葫芦娃'
# 这个是实例方法
#在类中定义,以self为第一个参数都是实例方法
#实例方法在调用时,python会将调用对象作为self传入
#通过类对象调用时,不会自动传self,必须手动传self
def test(self):
print('test方法')
#类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
#类方法可以通过类去调用,也可以通过实例调用
@classmethod #类方法
def test2(cls):
print('test2方法',cls)
#静态方法:不常用
#静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数,
#静态方法一般都是些工具类方法,和当前类无关。
@staticmethod
def test3():
print('test3方法')
a=A()
#实例属性:通过实例对象添加的属性
a.count=5
print('a',a.count)#a 5
print('A',A.count)#A 0,修改实例属性不会影响到类属性
A.count=5
print('a',a.count)#a 5,修改类属性会影响到实例属性
print('A',A.count)#A 5
a.test()#实例对象在调用,'test方法'
A.test(a)#类对象在调用,必须传入实例参数,'test方法'
A.test2()#test2方法 <class '__main__.A'>
a.test2()#test2方法 <class '__main__.A'>
A.test3()#test3方法
a.test3()#test3方法
八、特殊方法(魔术方法)
特殊方法都是以__开头__结尾的方法,特殊方法不需要自己调用。
'''
类的基本结构:
class 类名([父类]):
#公共属性...
#对象的初始化方法
#def __init__(self,...):
#...
#其他方法
#def method(self,...):
#...
'''
#尝试定义一个表示车的类
class Car():
def __init__(self,name,color):
self.name=name
self.color=color
def run(self):
print('汽车开始跑了...')
def laba(self):
print('%s,嘀嘀嘀...'%self.name)
c=Car('大奔','黑色')
print(c.name,c.color)
c.laba()
九、类的封装
我们要增加数据的安全性
1、属性不能随意修改(让你改你才能改,不让你改你改不了)
2、属性不能改为任意值
如何隐藏对象中的一个属性?
将对象的属性名,修改为一个外部不知道的名字。
如何获取(修改)对象当中的属性?
我们可以提供一个getter和setter方法,可以访问和修改属性。
一般情况下,使用_开头的属性都是私有属性,没有特殊情况不要修改私有属性。
可以为对象的属性使用双下划线开头,__XXX
双下划线开头的属性是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过外部访问。
本质上,双下划线开头的属性是python自动为属性改了一个名字。
实际改的名字是:_类名__属性名
'''
我们要增加数据的安全性
1、属性不能随意修改(让你改你才能改,不让你改你改不了)
2、属性不能改为任意值
---->>>>类的封装
'''
封装是面向对象的三大特性之一,封装指的是隐藏对象中一些不希望被外部访问到的属性或方法。
'''
如何隐藏对象中的一个属性?
将对象的属性名,修改为一个外部不知道的名字。
'''
class Dog:
def __init__(self,name):
self.hidden_name=name#弱封装,name不直接访问
def speak(self):
print('hello,my name is %s'%self.hidden_name)
d=Dog('二哈')
d.name='凯撒'
d.speak() #
d.hidden_name='凯撒'
d.speak()
'''
返回值:
hello,my name is 二哈
hello,my name is 凯撒
'''
'''
如何隐藏对象中的一个属性?
将对象的属性名,修改为一个外部不知道的名字。
如何获取(修改)对象当中的属性?
我们可以提供一个getter和setter方法,可以访问和修改属性。
'''
class Dog:
def __init__(self,name):
self.hidden_name=name#弱封装,name不直接访问
def speak(self):
print('hello,my name is %s'%self.hidden_name)
def get_name(self):
#get_name()用来获取对象的属性
return self.hidden_name
def set_name(self,name):
self.hidden_name=name
d=Dog('二哈')
print(d.get_name())
d.speak()
d.set_name('凯撒')
print(d.get_name())
d.speak()
'''
返回值:
二哈
hello,my name is 二哈
凯撒
hello,my name is 凯撒
'''
使用封装,确实增加了类定义的复杂度,但是它确保了数据的安全性。
1、隐藏了属性名,使调用者无法随意修改对象当中的属性;
2、增加了getter和setter方法,可以很好的控制属性是否是只读的;
(1)如果希望属性是只读的,则可以直接setter方法;
(2)如果希望属性不能被外界访问,则可以直接去掉getter方法;
3、使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的;
4、可以在读取属性和设置属性做一些其他的操作。
'''
一般情况下,使用_开头的属性都是私有属性,没有特殊情况不要修改私有属性。
可以为对象的属性使用双下划线开头,__XXX
双下划线开头的属性是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过外部访问。
本质上,双下划线开头的属性是python自动为属性改了一个名字。
实际改的名字是:_类名__属性名
'''
class Person:
def __init__(self,name):
self.__name=name
def get_name(self):
return self.__name
def set_name(self):
self.__name
def set_name(self,name):
self.__name=name
p=Person('金刚')
print(p.get_name())
p.__name='凯撒'
print(p.get_name())
p._Person__name='凯撒' #根据python中的隐藏名格式修改属性
print(p.get_name())
'''
返回值:
金刚
金刚
凯撒
'''
class Person:
def __init__(self,name):
self._name=name
@property #将方法变属性
def name(self):
print('该方法执行了')
return self._name
@name.setter #将方法用属性的模式,可更改
def name(self,name):
self._name=name
p=Person('金刚')
print(p.name)
p.name='凯撒'
print(p.name)
'''
返回值:
该方法执行了
金刚
该方法执行了
凯撒
'''
十、类的继承
1、继承的简介
(1)类的继承提高了代码的复用性;
(2)让类与类之间产生了关系,有了这个关系,才有了多态。
(3)继承是面向对象的三大特征之一:直接继承父类中的属性和方法
#要定义一个狗类:
#1、直接修改动物类-->修改起来比较麻烦,会违反ocp元组。
#2、直接创建一个新类(狗类)
class Animal:
def run(self):
print('动物跑...')
def sleep(self):
print('动物睡觉...')
#在定义类时,可以在类名后面加上括号,括号内指定的是当前类的父类(超类、基类、super)
class Dog(Animal):
def __init__(self):
# self._name=name
# self._age=age
pass
def run(self):
print('狗跑...')
a=Dog()
a.run()
#如果在创建类的时候省略了父类,则默认父类是object
#object是所有类的父类,所有父类都继承object
class Person(object):
pass
print(isinstance(Person,object))
2、方法重写
如果在子类中有和父类重名的方法,通过子类的实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点称之为方法的重写(覆盖override)。
当我们去调用一个对象的时候,会优先去当前对象寻找时候就有该方法,如果有,直接调用;如果没有,则去当前对象的父类中去寻找,如果有直接调用父类中的方法;如果没有,则去父类中的父类寻找,如果有直接调用,以此类推,直到找到object;如果在object中也没有,则报错。
class A(object):
def test(self):
print('AA...')
class B(A):
def test(self):
print('BB...')
class C(B):
def test(self):
print('CC...')
c=C()
c.test()
3、super()
父类中的所有方法都会被子类继承,包括特殊方法。
#定义一个动物类
class Animal:
def __init__(self,name):
self._name=name
def run(self):
print('动物跑...')
def sleep(self):
print('动物睡觉...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name=name
class Dog(Animal):
def __init__(self):
self._name=name
self._age=age
def run(self):
print('狗跑...')
4、多重继承
语法:类名.__bases__可以用来获取当前类的所有父类,在python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类。
在开发中没有特殊情况,尽量避免使用多重继承,因为多重继承会让我们代码过于复杂,如果多个父类中有同名的方法,则会在第一个父类中寻找,然后找第二个…前边的会覆盖后面的。
class A(object):
def test(self):
print('A...')
class B(object):
def test2(self):
print('B...')
class C(A,B):
pass
c=C()
c.test()
十一、类的多态
多态:字面上的理解就是多种形态,一个对象可以以不同的形态去呈现。
例如:
用len()函数来检查不同对象类型的长度,其实就是面向对象的特征之一,之所以有len()这个函数能获取长度,是因为这些对象中具有一个特殊方法__len__;换句话说只有对象中有__len__特殊方法,就可以通过len()方法来获取它的长度。
class A:
def __init__(self,name):
self._name=name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name=name
class C:
pass
#定义一个函数
def speak(obj):#只要类中有共同的属性,就可以共同执行
print('你好%s '%obj.name)
#在speak2函数中做了一个类型检查,也就是只有obj是A类型的对象的时候,
#才可以正常使用,其他类型的对象无法使用该函数,这个函数起始就违反了多态
#违反了多态的函数,只适用于一种类型的对象,无法处理其他类型的对象,、
#这样导致函数的适用性非常差。
def speak2(obj):
if isinstance(obj,A):
print('你好%s '%obj.name)
a=A('葫芦娃')
b=A('钢铁侠')
speak(a)
十二、面向对象的三大特征
封装:确保对象中的数据更安全,
继承:保证了对象的可扩展性;
多态:保证了程序的灵活性。