面对对象的简介
什么是对象?
对象就是内存中存储指定数据的一块区域,实际上对象就是一个容器,专门用来存储数据
对象的结构
id(标识)—用来标识对象的唯一性,每个对象都有唯一的id,指向一个内存地址值,id是由解释器生成的,id就是对象的内存地址
type(类型)—决定对象有哪些功能,通过type()函数可以查看对象的类型
value(值)—对象中储存的具体数据
对象分为可变对象和不可变对象
可变对象:list,dict,set
不可变对象:Number(int,float),str,Tuple,bool
面对对象
所谓面对对象简单理解就是语言中所有的操作都是通过对象来进行的
1、面对对象是一种思考问题的方式,面对象是一种思想
2、面对对象将实物变的简单化
面向过程和面向对象的比较
面对过程是将一个功能分解成一个个小的步骤,但这种编程方式往往只适用于一个功能,要实现别的功能时,需要编写新的代码,复用性比较低
面向过程编程比较符合人的思维,编写起来比较容易
面向对象容易维护,方便复用
面向对象,编写起来比较麻烦
类的简介
类简单理解就是一张图纸,在程序中需要根据类来创建对象
定义一个类
语法:
class 类名([父类]):
代码块
class MyClass:
pass
mc=MyClass()#用MyClass这个类创建一个对象mc,mc就是MyClass的实例
#isinstance()函数用来检查一个对象是否是一个类的实例
result=isinstance(mc,MyClass)
对象的创建流程
类是什么?
类也是一个对象,类是一个用来创建对象的对象
#代码接上一个代码块
print (id(MyClass),type(MyClass))
通过上面的代码块可以看出类是一个type类型的对象
通过MyClass创建出来的类都是空类
我们也可以像对象中添加变量,对象中的变量称之为属性
类的定义
类和对象都是对现实生活中实物或程序内容的抽象
实际上所有的事务都是由两部分组成:1、数据(s属性),2、行为(方法)
定义一个非空的类
class Person:
#在类的代码中,可以定义变量和函数
#类中的变量将会成为所有实例的公共属性
#所有的实例都可以访问这些变量
a=10
b='pyhton'
#在类中也可以定义函数,类中的函数称之为方法,该类的实例都可以访问
#调用类中的函数时,函数有几个形参就要传几个实参
#类中的方法至少要定义一个形参(self)
def speak(self,name):
#self这个蚕食就是调用方法的对象本身
name='葫芦娃'
print('你好我是%s',name)
p1=Person()#----创建一个类的实例
p1.speak()#----调用类中的方法
属性和方法
当调用属性和方法的时候,解析器会先在当前的对象中寻找是否有该属性或方法,若有则返回当前对象的属性值;
若没有则去当前对象中的类对象寻找,有则返回类对象中的属性值,若无则报错
类对象和实例对象都可以保存属性和方法
如果某个属性或方法是所有类共享的则保存到类对象当中
若干某个书讯或方法是某个实例特有的则保存到实例对象当中
一般情况属性保存到实例对象中,方法保存到类对象中
上面一个代码块中,name是每个属性都不相同的,所以在定义类的时候可以将类里函数改写为如下
class Person:
def speak(self,name):
#self这个蚕食就是调用方法的对象本身
print('你好我是%s',name)
p1=Person()
p1.name='葫芦娃'
p1.speak()
特殊方法(也称为魔术方法)
该方法都是以双下划线(’__’)开头,双下划线结尾
该方法的特点是不需要自己调用
上面的代码有一个问题就是在调用方法之前都需要给name这个变量赋值
如果忘记赋值的话就会导致调用方法时报错。这个问题就可以同在类里面引入一个特殊方法来解决
class Person():
def __init__(self,name):
self.name=name
def speak(self):
print('你好我是%s'%self.name)
p1=Person('葫芦娃')#----使用特殊方法之后,在创建实例的时候给name属性赋值
p1.speak()
所以类的基本结构
class 类名([父类]):
公共属性
对象的初始化方法
_init_(self,形参1,…)
其他方法
封装
封装是面向对象的三大特性之一
封装指隐藏对象中一些不希望被外部访问到的属性或方法
如何隐藏对象中的一个属性
将对象的属性名,修改为一个外部不知道的名字
class Person():
def __init__(self,name):
self.hiddenname=name
#hiddenname就是对属性进行隐藏
def speak(self):
print('你好我是%s'%self.hiddenname)
p1=Person('葫芦娃')
p1.speak()
如何获取(修改)对象当中的属性
可以提供一个getter和setter方法可以访问和修改属性
class Person:
def __init__(self,name,age):
self.hiddenname=name
self.hiddenage=age
def speak(self):
print('你好,我是%s,今年%d'%(self.hiddenname,self.hiddenage))
def getter_name(self):#获取name
return self.hiddenname
def setter_age(self,age):#修改age
if age>0:#对数据的校验
self.hiddenage=age
def getter_age(self):#获取age
return self.hiddenage
p1=Person('葫芦娃',20)
p1.speak()
print(p1.getter_name())
p1.settet_age(-10)
print(p1.getter_age())
使用封装增加了类定义的复杂度,但是确保了数据的安全性
1、隐藏了属性名,使调用者无法随意修改对象当中的属性
2、增加了getter和setter方法,可以很好的控制属性是否是只读
如果希望属性是只读的,则可以直接去掉setter方法
如果希望属性不能被外界访问,则可以直接去掉getter方法
3、使用setter方法设置属性,可以增加数据的验证,确保数据值的正确
4、可以在读取属性和设置属性时做一些其他操作
可以为对象的属性使用双下划线(__)开头
双下划线开头的属性是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过外部访问
一般情况使用单下划线开头的属性都是私有属性,没有特殊情况不要修改私有属性
@property(方法变属性)
class Person:
def __init__(self,name,age):
self.hiddenname=name
self.hiddenage=age
@property
def getter_name(self):#获取name
return self.hiddenname
p1=Person('小明',20)
print(p1.getter_name)
通过上面代码可以看出添加@property装饰器后,将方法转换为属性,可以像调用属性一样调用方法
继承
继承是面向对象编程的三大特性之一
继承的作用:
1、提高代码的复用性
2、让类与类之间产生关系,有了这层关系才会有多态
class A(object):
def test1(self):
print('A中的test1方法')
class B():
def test2(self):
print('B中的test2方法')
class C(A,B):
def test1(self)
print ('C中的test1方法')
def test3(self):
print('C中的test3方法')
print(issubclass(B,object))#判断一个类是不是另一个类的子类
c=C()
c.test1()
c.test2()
c.text3()
print(C.__bases__)#获取当前类的所有父类
#python中是支持多重继承的,也就是说可以为一个类同时指定多个父类
#如果多个父类中有同名方法,则会在第一个父类中选择,然后找第二个...前面覆盖后面的
通过上面的代码可以看出:
1、在定义类的时候,可以在类名后面加括号,括号内指定的是当前类的父类(超类,基类,super)
2、如果在创建类的时候,省略父类,默认父类为object
object是所有类的父类,所有类都继承object
3、如果在子类中有和父类重名的方法,通过子类的实例去调用时,会调用子类的方法而不是父类的方法,这个特点称之为方法的重写(覆盖,overrise)
4、当调用一个方法的时候,会优先去当前对象寻找是有有该方法,如果有直接调用;如果没有则去当前独享的父类中寻找,若有直接调用父类中的方法;如果没有则去父类的父类寻找,若有则直接调用,以此类推,直到找到object,如果依然没有则报错
super()
class A:
def __init__(self,age):
self.name='小明'
class B(A):
def __init__(name):
super().__init__(name)#直接调用父类的__init__来初始化父类中的属性
def get_name(self):
return self.name
b=B()
print(b.get_name())
通过上面代码可以看出,supper()函数可以用来获取当前类的父类的
并且通过supper()返回的对象,调用父类方式时不需要传递self
多态
面向对象编程的三大特性之一
概念:字面上理解就是多种形态,程序中则是一个对象可以以不同是形态去呈现
多态使面向对象编程更加具有灵活性
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 B:
def __init__(self,name):
self._name=name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name=name
def speak(obj):
print('你好,%s'%name)
def speak1(obj):
if isinstance(obj,A):#判断obj是不是A的实例
print('你好,%s'%name)
a=A('小明')
b=B('小小')
speak(a)
speak(b)
speak1(a)
speak1(b)#不会有执行结果
通过上面代码可以看出speak1中做了一个类型判断,检查obj是不是A类的对象。只用判断为True才能使用改函数,此函数就违反了多态
违反了多态的函数,只适用于一种类型的对象,无法处理其他类型的对象,这样导致函数的适用性非常差
总结:面向对象的三大特征
封装–确保对象中数据的安全
继承–保证对象的可扩展性
多态–保证程序的灵活性
类中的属性和方法
class A:
a=0#类属性:直接在类中定义的属性
def __init__(self):#实例方法:在类中定义,以self为第一个参数的
self.name='梅梅'
def test(self):
print('我的test方法')
@classmethod
def test2(cls):#类方法:第一个参数是cls,会被自动传递,cls就是当前的类对象
print('我的test2方法')
print(cls.a)
@staticmethod
def test3():#静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
#静态方法一般都是些工具方法,和当前类无关
print('我是test3方法')
b=A()
print(b.a)
print(A.a)
#类属性可以通过类或者类的实例来访问
b.a=10
print(b.a,A.a)#只能修改实例属性,不能修改类属性
A.a=20
print(b.a,A.a)#即能修改类属性也能修改实例属性
#实例属性
print(b.name)#打印结果为“梅梅”
print(A.name)#打印报错
b.name='xiaoming'
A.name='xiaoming'
print(b.name)#打印结果为“xiaoming”
print(A.name)#打印报错
#通过上面两个打印可以看出实例属性只能通过实例调用和修改
#实例方法
b.test()#执行结果为'我的test方法'
A.test()#执行结果会报错
A.test(b)#执行结果为'我的test方法'
#通过上面代码可以看出实例方法既可以被实例调用也可以被类调用,但是实例调用时程序自动传入self值,类调用时需要手动传入self值
#类方法
A.test2()#执行结果'我的test2方法'
b.test2()#执行结果'我的test2方法'
#通过上面代码可以看出,类方法既可以通过类进行调用,同时也可以通过实例进行调用,调用方法时cls参数都是自动传入
#静态方法
A.test3()
b.test3()
#通过上面代码可以看出静态方法类和实例都可以进行调用