一:面向对象基础
1.类和对象:
(1)类
类是对一系列具有相同特征和行为的事物的统称,是一个抽象的概念,不是真实存在的事物,而对象就是根据类来创建的,有类才有对象
- 特征 --> 属性 --> 变量
- 行为 --> 方法 --> 函数
类的创建:
class 类名():
特征 --> 属性:变量
行为 --> 方法:函数
pass
注意: 类名要满足命名规范,最好遵循驼峰命名习惯
#定义一个类
class Dog():
#特征(属性):用变量去描述这个事物所具有的特征
head = 1 #类属性
#行为(方法):用函数去描述行为,实例化方法
def eat(self): #self是必备参数,是自带的,表示实例对象本身
print("吃")
def drink(self):
print("喝")
print(dog) #此时只有类没有对象它是没有实质性意义的
注意:
- 当有多个特征或行为时,就定义多个变量或函数,当然,特征与行为并不做固定要求,可以有也可以没有;但是一个规范的类,行为都是要有的
self
不写会报错;self
表示实例对象 -->self
是实例对象;实例对象也是self
。所以无需给self
传参。self
是动态的,哪个对象调用它,它就是哪个对象
(2)对象
对象是类所创建出来的真实存在的事物
对象的创建:实例化对象
one = Dog() #实例化对象:dog是一个类的统称;而one是一个具体的实例
print(one)
one.eat() #通过实例对象访问类里面的方法(调用类里面的方法)
print(one.head) #通过实例对象访问事物的特征
注意: 通过一个类可以去创建无数个实例对象;注意区分属性和方法,属性不带括号,而方法带括号
#在外部定义属性
one.xxx = 10 #实例属性
print(one.xxx)
注意: 虽然可以在外部定义属性,但是不建议,因为那是实例对象的属性,并不是类的属性, 类属性是所有的实例对象都可以访问到的,是公共的属性;而实例属性只有当前的实例对象才可以访问到,是私有属性,其它的实例对象访问会报错。方法只能定义在类里,不可在外部添加
2.魔法方法:
在Python中,__xx__()
的函数叫做魔法方法,指的是具有特殊功能的函数,当它满足条件时会自动执行
__init__()
方法:初始化对象
class Dog():
az = 4
def __init__(self): #在实例化对象的时候自动执行,不需要单独调用
print("正在初始化~~~")
def eat(self):
print("吃")
dahei = Dog() #只要实例化对象就会触发执行 __init__() 魔法方法
print(dahei.az)
dahei.eat()
#定义实例属性:
# 1.实例对象.属性名 = 值
# 2.在类里定义实例属性 -- 使用 __init__() 魔法方法定义:
class Dog():
az = 4
def __init__(self,name1,price1): #name1与price1都是形参
self.name = name1 #self就是实例对象,所以相对于 -- 实例对象.属性名(定义的名字) = 值
self.price = price1 #这里的name1与price1是用来接收传入形参的具体的值
print("正在初始化~~~")
def eat(self):
print("吃")
dahei = Dog("边牧",80000000)
dahei = Dog(name1="边牧",price1=80000000)
print(dahei.name)
print(dahei.price)
注意: self
这个参数不用传参,但定义的其它参数是一定要去传参的,不然会报错
__str__()
方法:
class Dog():
az = 4
def __init__(self,name1,price1):
self.name = name1
self.price = price1
print("正在初始化~~~")
def __str__(self): #当输出实例对象的时候自动执行
# return self.name #返回指定的值给到实例对象
return "DuDu" #也可以返回任意的值
def eat(self):
print("吃")
dahei = Dog("边牧",80000000)
print(dahei) #输出实例对象
__del__()
方法:
class Dog():
az = 4
def __init__(self,name1,price1):
self.name = name1
self.price = price1
print("正在初始化~~~")
def __str__(self):
return self.name
def __del__(self): #当实例对象被销毁的时候自动执行
print(f"{self.name}被销毁了———")
def eat(self):
print("吃")
dahei = Dog("边牧",80000000)
print(dahei)
注意: 当程序运行结束后,一些用不到的数据会被Python自动销毁掉(或者手动用 del
语句销毁),此时就会触发 __del__()
方法
二:面向对象进阶
1.面向对象的特性:
面向对象编程有三个特性:封装;继承;多态
(1)封装
在面向对象的程序设计中,某个类把所需要的数据(类的属性)和对数据的操作(类的行为)全部都封装在类中,分别称为类的成员变量和方法(成员函数),这种把成员变量和成员函数封装在一起的编程特性称为封装
(2)继承
继承是指可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行拓展
(3)多态
多态指的是一类事物有多种形态。如:
- 序列类型的多种形态:字符串;列表;元组等
- 动物类型的多种形态:猫;狗;猫头鹰;乌鸦等
多态性是允许你将父对象设置成为和一个或更多的它的子对象相等的技术,赋值后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作
2.封装:
封装分为数据封装和方法封装
(1)数据封装:就是把属性中具体的值给隐藏起来,对外部显示的只有属性名。封装数据的主要原因是保护隐私
(2)方法封装:就是在外部只需要通过方法名就能调用方法,不需要了解方法中具体的细节。封装方法的主要原因是隔离复杂度
封装的两个层面
(1)第一层面的封装:
创建类和对象时,分别创建二者的名称空间,只能通过 类名.
或 对象.
的方式访问里面的名字
(2)第二层面的封装:
在类中把某些属性和方法隐藏起来,或者定义为私有,只在类的内部使用,在类的外部无法访问,或者留下少量的接口函数供外部使用
class People(): #类是一个模型,不可以直接访问到行为,只有实例对象才可以调用方法
#属性和方法是封装在类里面的
age = 18 #类属性 --> 用变量定义:是对于事物特征的描述;这里的类并没有通过实例对象去定义,所以是类属性;任何对象都可以访问到
#通过魔法方法定义实例属性
def __init__(self,name): #因为定义了一个形参,所以在后面实例化对象时一定要传参
self.name1 = name #self == i;self表示实例对象本身,也就是实例对象。实例对象.属性名
def eat(self): #方法 --> 用函数定义:是对于事物行为的描述
self.name1 = "张三" #在这里也可以定义实例属性,只要是通过 实例对象.属性名 去定义的就可以
print("吃")
#通过对象去定义实例属性
i = People("张三") #实例化对象
i.name1 = "张三" #i定义的实例属性只有i可以访问到
print(i.name1)
#现在的属性可以通过类名访问或修改;还可以通过实例对象访问或修改,这样的话会使这个类很不安全,把数据完全的暴露在外面
#通过类名访问属性
print(People.age)
#通过类名修改属性
People.age = 20
print(People.age)
#通过实例对象访问属性
print(i.age)
#通过实例对象修改属性
i.age = 30
print(i.age)
#私有化,类的私有属性:将类中的属性与方法封装起来就叫封装;封装是为了不让数据暴露在外面,被外部所修改
class People():
__age = 18 #定义私有属性:在变量名的前面加上两个下划线;私有属性只能在类的里面去使用,私有属性在外部不可被访问或修改,不然会发生报错
def eat(self):
print("吃")
def get(self): #定义的函数接口,拿到age值,可以在外部调用该方法去访问私有属性,但不能修改
print(self.__age) #函数接口就是间接的去访问
i = People()
#print(i.__age) #此时再去访问属性会发生报错
i.get() #通过实例对象去调用这个接口,可以访问但不能做修改
People().get() #也可以直接通过类去调用这个接口,可以访问但不能做修改
#私有化:类的私有方法:存储重要数据,不让别人看到
class People():
age = 18
def __hob(self): #定义私有化方法,禁止别人访问,在函数名前加上两个下划线;若想要访问,也需要在定义一个函数接口
print("xxx")
def a(self): #定义的函数接口
return self.__hob() #return后面可以放任何数据,此时返回的是执行__hob()函数
i = People()
i.a() #通过实例对象在外部调用函数接口,可以访问但不能做修改
People().a() #直接通过类去调用这个函数接口,可以访问但不能做修改
注意: 个人理解:封装就是让数据私有化,让数据更加的安全
3.继承:
- 当我们定义一个
class
的时候,可以从某个现有的class
中继承属性和方法,新的class
称之为子类,而被继承的class
称之为父类;基类或超类。父类可以被子类继承,但子类不能继承其它的子类 - 在Python中,
object
类是所有类的父类,不管写没写,都会自动继承;Python3
中默认继承都是object
类,object
类是顶级类;终极类或基类,只要定义一个类都会自动继承Python中的object
类;而其它的子类也叫做派生类
class 派生类名(object): #当括号为空时,默认继承object类
pass
(1)继承的特点
-
在Python中,如果父类和子类都重新定义了构造方法
__init__( )
,在进行子类实例化的时候,子类的构造方法不会自动调用父类的构造方法,必须在子类中显示调用 -
如果需要在子类中调用父类的方法:需要以
父类名.方法名(self)
这种方式调用,以这种方式调用的时候,注意要传递self
参数过去;另一种方法是使用super()
,直接使用super().方法名
-
Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去父类中找)
-
对于继承关系,子类继承了父类所有的公有属性和方法,可以在子类中通过父类名来调用,而对于私有的属性和方法,子类是不进行继承的,因此在子类中是无法通过父类名来访问的
-
Python支持多继承,能够让一个子类有多个父类
-
如果有多个父类,多个父类名之间用逗号隔开
-
如果子类没有重新定义构造方法
__init__()
,它会自动调用第一个父类的构造方法,以第一个父类为中心 -
如果子类重新定义了构造方法,需要显示去调用父类的构造方法,此时调用哪个父类的构造方法由你自己决定
注意: 上面的特点不重要,直接看下面的代码就行
(2)单继承
把一个类作为另一个类的父类,就是把这个类传给另一个类,这就叫做单继承;当继承后除子类中自己的属性和方法外,父类中的所有属性和方法子类也都可以使用
#一个学生继承一个老师类
class Teacher(object):
def hobby(self):
print('继承了老师的爱好-python')
class Student(Teacher): #继承关系:括号里填入被Student类所继承的类的类名,表示这个类被Student类继承
def __init__(self,name):
self.name = name
def info(self):
print("我是学生{}".format(self.name))
#实例对象,调用Teacher类中的方法
i = Teacher()
i.hobby()
#实例对象,调用Student类中的方法
f = Student("张三")
f.info()
#使用父类中的方法
f.hobby()
(3)多层继承
在父类中,父类继承一个父类,我将其称为祖父类;只要是被继承了那么父类中的所有方法都可以调用,包括祖父类中的方法与属性(私有属性和方法除外)
#定义父类
class Teacher():
age_ = 28
def eat(self):
print("吃")
#定义子类
class Student(Teacher):
def __init__(self,name):
self.name1 = name
age = 18
def drink(self):
print("喝")
i = Student("张三")
i.drink()
print(i.age_)
class Dog(Student):
pass #pass用来保证代码的完整性
#f = Dog() #此时运行Student类里面的 __init()__ 方法,因为Dog类中并没有写入Student类中 __init()__ 方法的name参数,所以发生报错
f = Dog("李四")
print(f.age_)
f.drink()
所有继承的关系:
- 在实例化对象的过程中会优先加载本类中的
__init()__
方法,如果本类中没有,就会去父类中找 - 父类的先后顺序:先去父类中找,如果没有的话再去祖父类中找,哪里有就运行哪里的
__init()__
方法,都没有的话会执行object
里面的__init()__
方法
mro()
方法:打印这个类的继承关系语法格式:
类名.mro()
class miao():
def __init__(self,age):
self.xiaohuang = age
print(self.xiaohuang) #注意:在一个类的方法中访问其它类的属性或方法需要使用self关键字
def play(self):
print(f"它今年{self.xiaohuang}了")
name = "小黄"
class People(miao):
pass
print(People.mro()) #输出People类的继承关系
i = People("18")
i.play()
(4)多继承
一个子类可以继承多个父类
class GrandFather():
def dudu(self):
print("!!!!!!!!!")
class Father(GrandFather):
def skill(self):
print("rap")
def play(self):
print("手机")
def da(self):
print("这是Father")
class Mother():
def skill(self):
print("唱跳")
def play(self):
print("代码")
def ma(self):
print("这是Mother")
class Son(Father,Mother): #多继承,先继承前面的在继承后面的,属于同级关系,继承一个之后就不能继承另一个了;要想一起继承,就用重载方法
def __init__(self,age):
self.age = age
def play(self):
print("篮球")
# 重载:重新加载父类中的方法;当子类中的方法名和父类中的方法名同名时,依旧能够执行父类中的同名方法
super().play() #重新加载父类:super().方法名;super()方法表示父类,意思是依次调用父类的方法,不能指定类去调用;也不可调用同类方法,同类方法只能用self调用
#注意:super()方法也可以调用父类的父类,也就是祖父类
super().dudu()
Father.play(self) #这个也是重载父类的方法;记得传参self
Mother.play(self) #重载第二个父类
GrandFather.dudu(self) #重载祖父类
print("除了玩手机还会玩电脑") #在父类的基础上对本类的play()方法进行新增,这就是重载的作用
def xiao(self):
print("这是Son;年龄{}".format(self.age))
son = Son("11")
son.xiao()
son.skill() #当多个父类中的方法名同名时,优先使用最前面的父类中的方法;当所有父类中都没有这个方法名时,会发生报错
son.play() #当子类中的方法名和父类中的方法名同名时,为重写;重写方法后就不会在去执行父类中的同名方法了
son.dudu()
#注意:私有属性和私有方法不可被继承,只能通过接口去调用
4.多态:
- 同一种事物的多种形态,不同的形态(对象)调用同一种方法能够实现不同的效果
- 指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数;有了多态,更容易编写出通⽤的代码,做出通⽤的编程,以适应需求的不断变化!
实现步骤:
- 定义⽗类,并提供公共⽅法
- 定义⼦类,并重写⽗类⽅法
- 传递⼦类对象给调⽤,可以看到不同⼦类执⾏效果不同
class Animal(): #父类
def eat(self):
pass
class Panda(Animal):
def eat(self):
print('吃竹子')
class Cat(Animal):
def eat(self):
print('吃老鼠')
class Rabbit(Animal):
def eat(self):
print('吃胡萝卜')
def animal(obj): #obj是形参,在这里传入的是子类的实例化对象
obj.eat()
rabbit1 = Rabbit()
panda1 = Panda()
# 传入不同的对象,实现不同的方法
animal(rabbit1)
animal(panda1)
注意: 同一个方法,不同对象调用方法,实现的功能不一样,这就是多态
#定义一个矩形类,拥有两个实例属性长和宽,以及计算周长和面积的两个方法
#第一种方式
class Ju():
__chang = int(input("请输入矩形的长:"))
__kuan = int(input("请输入矩形的宽:"))
#周长:(长加宽)x 2
#面积:长 x 宽
def chang(self):
a = (self.__chang + self.__kuan) * 2
print("周长是:",a)
def kuan(self):
i = self.__chang * self.__kuan
print("面积是:",i)
zhouchang = Ju()
zhouchang.chang()
zhouchang.kuan()
#第二种方式:
class Ju():
def __init__(self,chang,kuan):
self.long = chang #实例属性
self.width = kuan #实例属性
def zhou(self):
a = (self.long + self.width) * 2
print(f"周长是:{a}")
def mianji(self):
b = self.long * self.width
print(f"面积是:{b}")
dudu = Ju(12,34) #传入参数长和宽
dudu.zhou()
dudu.mianji()
注意: self
就是实例对象,通过实例对象去定义的属性都是实例属性