目录
编程思路
在python中,有3种编程思路。分别是
- 面向过程编程
- 面向函数编程
- 面向对象编程
面向过程编程
即是根据逻辑从上而下的写代码,这种编程思路虽然十分简单,但是代码多起来后则会十分的复杂,工作量也会大幅增加!
面向函数编程
即是根据需求将功能相同的代码封装成一个个的函数,需要时调用即可,可以减少代码的重复工作量,但是如果写的函数过多的话还是会使得代码十分的复杂。
面向对象编程
相比于面向函数编程,则是一个更大的封装,根据职责在一个对象中封装更多的函数。更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路。但是这种思路比其它两种思路相对来说更难。
面向对象编程
面向对象编程——Object Oriented Programming 简写 OOP。
可以把它理解为上帝式思维,世间万物皆为对象,不存在的也可以创建出来。在面向对象编程的过程中,有两个重要的组成部分:类与对象。
类与对象
类
-
对一系列具有相同特征和行为的事物的统称
-
抽象的概念
-
不是真实存在的事物
-
-
特征既是属性
-
行为即是方法
对象
- 是由类创建出来的真实存在的事物
在编程中,是先创造出一个类,然后再用类创造出对象。一个类可以创建出多个对象。打个比方就是手机与小米、华为等品牌的关系,是先创造出手机,然后再通过手机创造出小米、华为等品牌,虽然小米和华为中有部分差别,但是大体上都是可以归为手机的。
定义类
类又分为经典类和新式类。
经典类:
不由任意内置类型派生出来的类,称之为经典类。
经典类格式:
class 类名:
代码
但是这个类型不常用,平时使用新式类即可。
新式类
与经典类的区别就是在类名后面多一个括号。
新式类格式:
class 类名():
代码
定义类名的规则与标识符命名规则相同。同时最好遵守大驼峰命名规则,增加代码可读性。
大驼峰命名规则:即是字与字之间开头字母使用大写区分分开。
创建对象
创建类后,就可以使用类创建对象,这个过程叫做实例化对象
格式:
对象名=类名()
class ni_hao(): # 创建一个你好的类
def fn(self): # 创建一个方法
print('你好') # 打印你好
xiao_ming = ni_hao() # 创建一个叫小明的对象
xiao_ming.fn() # 调用类里面的方法
self
在类里面定义方法时,大部分的编译器会自动在函数括号中加一个self。self表示的是调用该函数的对象。调用实例函数的时候,self不需要手动传参,系统会自动传递当前的对象 , 哪个对象调用了方法,方法里的self指的就是谁。
添加获取对象属性
属性即特征。假设创造一个洗衣机的类,那么洗衣机的高和宽等数据就是这个类的属性。
类外面添加属性
格式
对象名.属性名=值
# 假设创造一个洗衣机的类
class Xi_Yi_Ji():
def fn(self):
print('洗衣服')
#类外面创造属性
Hai_Er = Xi_Yi_Ji()
Hai_Er.gao = 400 # 高为400
Hai_Er.kuan = 300 # 宽为300
print(f'Hai_Er的高为{Hai_Er.gao}') # Hai_Er的高为400
print(f'Hai_Er的高为{Hai_Er.kuan}') # Hai_Er的高为300
Hai_Er.fn() # 洗衣服
如果需要更改也是一样。
类里面获取属性
格式
self.属性名=值
self则表示对象!
# 假设创造一个洗衣机的类
class Xi_Yi_Ji():
def fn(self):
print('洗衣服')
def info(self):
print(f'该洗衣服的高为{self.gao}') #打印高
print(f'该洗衣服的宽为{self.kuan}') #打印宽
hai_er=Xi_Yi_Ji()
hai_er.gao=400 #外部定义属性
hai_er.kuan=300
hai_er.info() #对象调用实例方法打印属性
注意:在类里面使用属性时,必须使用self表示对象。
魔法方法
在方法中,__函数名__()的函数叫做魔法方法,指的是具有特殊功能的函数。
__init__()(初始化对象)
可以初始化对象属性。在创建对象时默认调用,不需要手动调用。在这里面定义的属性为实例属性
格式:
def __init__(self)
代码#添加属性
例子
class Xi_Yi_Ji():
def __init__(self): #初始化属性
self.gao = 300 #高
self.kuan = 400 #宽
def info(self):
print(f'该洗衣服的高为{self.gao}') # 打印高
print(f'该洗衣服的宽为{self.kuan}') # 打印宽
hai_er=Xi_Yi_Ji() #创建对象
hai_er.info() #对象调用实例方法
'''
该洗衣服的高为300
该洗衣服的宽为400
'''
如果需要在类外面修改属性,对象名.属性名=值直接修改即可,修改后只是针对这个对象的,类的初始属性不会发生改变。
class Xi_Yi_Ji():
def __init__(self): #初始化属性
self.gao = 300 #高
self.kuan = 400 #宽
def info(self):
print(f'该洗衣服的高为{self.gao}') # 打印高
print(f'该洗衣服的宽为{self.kuan}') # 打印宽
hai_er=Xi_Yi_Ji() #创建对象
hai_er.gao=100 #将高修改为100
hai_er.info() #对象调用实例方法
mei_di=Xi_Yi_Ji() #在创建一个对象
mei_di.info() #调用实例方法
'''
该洗衣服的高为100
该洗衣服的宽为400
该洗衣服的高为300
该洗衣服的宽为400
'''
在__init__()的括号中,也是可以传递参数的,可以更方便的针对不同对象初始化不同的属性,传参数方法与函数的相同。因为__init__()本身也是一个函数,但是形式参数中的self不能缺少。
class Xi_Yi_Ji():
def __init__(self,gao,kuan): #初始化属性
self.gao = gao #高
self.kuan = kuan #宽
def info(self):
print(f'该洗衣服的高为{self.gao}') # 打印高
print(f'该洗衣服的宽为{self.kuan}') # 打印宽
hai_er=Xi_Yi_Ji(100,200) #创建对象
print('hai_er属性:')
hai_er.info() #对象调用实例方法
mei_di=Xi_Yi_Ji(400,500) #在创建一个对象
print('mei_di属性:')
mei_di.info() #调用实例方法
'''
hai_er属性:
该洗衣服的高为100
该洗衣服的宽为200
mei_di属性:
该洗衣服的高为400
该洗衣服的宽为500
'''
__str__()(返回字符串)
在不加__str__()的情况下,创建对象后直接打印这个对象输出结果是这个对象的内存地址。而使用后则会打印__str__()中返回的字符串。
class lei1():
def info(self):
print('你好')
dui_xiang=lei1()
print(dui_xiang)
#输出结果<__main__.lei1 object at 0x00000273A3B10BE0>
class lei2():
def info(self):
print('你好')
def __str__(self):
return '这是一个说明'
dui_xiang=lei2()
print(dui_xiang)
#输出结果这是一个说明
注意:__str__()返回的内容一定是一个字符串,其他的数据类型都会报错。
__del__()(删除对象)
当删除一个对象时,python解释器也会默认调用__del__()方法。类似一个回收机制。但是平时如果需要直接删除的话使用del也可以。不需要调用__del__()方法。
class Person(object):
def __init__(self,name):
self.name = name
def __del__(self):
print("实例对象:%s"%self.name,id(self))
print("python解释器开始回收%s对象了" % self.name)
print("类对象",id(Person)) #类对象 2491590992752
zhangsan = Person("张三")
print("实例对象张三:",id(zhangsan)) #实例对象张三: 2491589158528
print("------------")
lisi = Person("李四")
print("实例对象李四:",id(lisi)) #实例对象李四: 2491587722304
'''
类对象 2096998362976
实例对象张三: 2097003914880
------------
实例对象李四: 2097002478656
实例对象:张三 2097003914880
python解释器开始回收张三对象了
实例对象:李四 2097002478656
python解释器开始回收李四对象了
'''
观察代码可以看出,当运行完print("实例对象李四:",id(lisi))时,后面没有代码了,所以运行结束,将所使用的内存都会进行删除回收,于是就运行了类中__del__()的语句。
直接删除对象
使用del即可。
格式
del 对象名
class lei():
def __init__(self,name):
self.name=name
def __del__(self):
print(f'{self.name}开始回收!')
name1=lei('张三')
name2=lei('李四')
del name2
'''
李四开始回收!
张三开始回收!
'''
使用del删除对象name2,也会默认调用__del__()。然后因为代码运行结束,系统会自动删除name1。
属性
类中的属性共有两种。分为实例属性和类属性
实例属性
就是在__init__()内部的属性叫做实例属性,表示对象的属性,只能被对象调用。
class Lei1(object):
def __init__(self):
self.name = '小明'
wo = Lei1()
print(wo.name) # 通过对象进行调用,输出结果为小明
wo.name = '哈哈' # 对实例属性进行修改
print(wo.name) # 输出结果为哈哈
print(Lei1.name) # 通过类进行调用
# AttributeError: type object 'Lei1' has no attribute 'name'
如果进行修改也很容易,调用后进行赋值即可。通过类进行调用的化会报没有该名称。
类属性
类属性就是类、对象拥有的属性,它被该类的所有实例对象所共有,可以使用类对象或实例对象访问。
class Lei1(object):
age = 18 # 类属性
def __init__(self): # 实例属性
self.name = '小明'
print(Lei1.age) # 通过类进行调用,输出结果18
wo = Lei1() # 创建对象
ni = Lei1() # 创建对象
print(wo.age) # 通过对象进行调用,输出结果18
wo.age = 40 # 通过对象对类属性进行修改
print(wo.age) # 输出结果为40
print(Lei1.age) # 输出结果为18
Lei1.age = 30 # 通过类对类属性进行修改
print(wo.age) # 输出结果为40
print(ni.age) # 输出结果为30
print(Lei1.age) # 输出结果为30
好处是:
- 记录某一项数据始终保持一致, 则定义类属性
- 只占一份内存,更加节省内存空间
对类属性进行修改时,通过类对类属性进行修改则会对这个类创建的对象有影响,而通过这个类创建的对象对类属性进行修改的话则只影响这个对象,不会对类本身有影响。
类方法
与类属性类似,可以使用对象和类进行调用,一般与类属性搭配使用。使用方法和装饰器有点像,在需要定义为类方法的方法前面加@classmethod,方法括号中的self改成cls。表示这个类。
class Lei1(object):
age = 18 # 类输出
@classmethod # 表示下面的方法为类方法
def info(cls):
print(cls.age)
Lei1.info() # 通过类进行调用,输出结果为18
wo = Lei1() # 创建对象
wo.info() # 通过对象进行调用,输出结果为18
静态方法
使用方法是在需要定义为静态方法的方法前面加@staticmethod。同时该方法的括号内不需要加如何参数,所以在静态方法内部不能调用实例属性和类属性。可以被类和对象调用。
class Lei1(object):
@staticmethod #静态方法
def info():
print('hello world')
Lei1().info() #hello world
wo=Lei1()
wo.info() #hello world
如果在静态方法中调用实例属性或者类属性,则会报错,为该变量未定义。
property的用法
作用是将动态方法转换为属性,当对象调用该属性时会自动执行该方法。使用方法是在动态方法前加@property,表示该方法转换为属性
class Lei(object):
def __init__(self):
self.__age = 0
# 不加的话这个是动态方法
def age1(self):
print('当前属性为', self.__age)
# 加了后表示这是属性
@property
def age2(self):
print('我是age2')
lei = Lei()
lei.age1() # 动态方法
print(lei.age2) # 属性
.setter
class Lei(object):
def __init__(self):
self.__age = 0
# 加了后表示这是属性
@property
def age1(self):
print('我是age1')
@age1.setter
def age2(self,new_age):
self.__age = new_age
print(self.__age)
lei = Lei()
print(lei.age1) # 属性
lei.age2 = 50
print(lei.age2)
property的用法
可以更方便的对私有属性进行获取和修改
class Lei(object):
def __init__(self):
self.__age = 0
def get_age(self):
print('获取私有属性了')
return self.__age
def set_age(self, new_age):
print('修改私有属性了')
self.__age = new_age
age = property(get_age, set_age)
lei = Lei()
lei.age = 50
print(lei.age)
面向对象的三大基本特征
- 继承
- 封装
- 多态
继承
如果两个或者两个以上的类具有相同的属性和方法,我们可以抽取一个类出来,在抽取出来的类中声明各个类公共的部分。被抽取出来的类称之为父类(超类、基类),两个或两个以上的类称之为子类 (派生类),他们之间的关系是 子类继承自父类 或者 父类派生了子类。
格式
class 字类名(父类名)
代码
例子:
class Lei1(object): # 父类
def fn(self):
print('你好')
class Lei2(Lei1): # 字类
pass
dui_xiang = Lei2() # 使用子类创建对象
dui_xiang.fn() # 输出结果你好
可以发现,在子类中没有定义如何函数,只是继承了父类中的函数。在使用子类创建对象后,该对象也就有了父类中的函数。
object
表示是 Python 中所有类的根类。一般在定义父类中使用。
单继承
一个父类继承给一个子类,这样的就叫做单继承。
class Lei1(object): # 父类
def fn(self):
print('你好')
class Lei2(Lei1): # 字类
pass
dui_xiang = Lei2() # 使用子类创建对象
dui_xiang.fn() # 输出结果你好
多继承
多个父类继承给一个子类,这样的叫做多继承。
格式
class 子类(父类1,父类2等)
代码
例子:
class Lei1(object): # 父类
def fn1(self):
print('你好')
class Lei2(object): # 父类
def fn2(self):
print('hello')
class Lei3(Lei1, Lei2): # 字类
pass
dui_xiang = Lei3() # 使用子类创建对象
dui_xiang.fn1() # 输出结果你好
dui_xiang.fn2() # 输出结果hello
如果父类中有相同名的函数,则按照子类括号中的父类顺序决定使用哪个父类的函数,那个父类在前,就使用哪个父类中相同函数名的函数。
class Lei1(object): # 父类
def fn1(self):
print('你好')
def a(self): # 相同函数
print('1')
class Lei2(object): # 父类
def fn2(self):
print('hello')
def a(self): # 相同函数
print('2')
class Lei3(Lei2, Lei1): # 子类,Lei2更靠前
pass
class Lei4(Lei1, Lei2): # 子类,Lei1更靠前
pass
dui_xiang1 = Lei3() # 使用子类Lei3创建对象
dui_xiang2 = Lei4() # 使用子类Lei4创建对象
dui_xiang1.a() #输出结果2
dui_xiang2.a() #输出结果1
同时调用多个父类的方法
在上面多继承的例子中,可以发现,多个父类继承一个子类中只可以调用类名括号中靠前的父类中的函数,如果需要调用靠后的父类方法,也很简单。只需要在子类中再定义一个函数,函数中进行调用父类的函数,如果父类中有初始化属性,则在调用父类函数前调用父类的属性以保证调用的是父类的属性
class Lei1(object): # 父类1
def __init__(self):
self.name = '我是父类1'
def fn(self):
print(self.name)
class Lei2(object): # 父类2
def __init__(self):
self.name = '我是父类2'
def info(self):
print(self.name)
class Zi_lei(Lei1, Lei2): # 子类
def __init__(self): # 子类的属性
self.name = '我是子类!'
def han_shu(self):
print(self.name) # 打印子类的属性
def han_Lei1(self): # 继承父类1的函数
Lei1.__init__(self) # 调用父类1的属性
Lei1.fn(self)
def han_Lei2(self): # 继承父类2的函数
Lei2.__init__(self) # 调用父类2的属性
Lei2.info(self)
da_chui = Zi_lei() # 创建对象为大锤
da_chui.han_shu()
da_chui.han_Lei1() # 父类1中的函数
da_chui.han_Lei2() # 父类2中的函数
'''
我是子类!
我是父类1
我是父类2
'''
上面代码中,父类中的函数并没有将多继承中例子一样,而是将父类中所有的函数的继承了过来。
子类重写父类重名的属性和函数
在子类中直接定义函数,如果有函数名相同的话在调用时就使用子类自己的函数内容,如果不相同则就是添加定义新的函数功能
class Lei1(object): # 父类
def __init__(self):
self.name = '小明'
def fn(self):
print(f'我叫{self.name}')
class Zi_lei1(Lei1): # 子类1,对父类进行修改
def __init__(self):
self.gao = 180
def fn(self):
print(f'我的身高有{self.gao}')
class Zi_lei2(Lei1): # 子类2,不做然后修改
pass
wo = Zi_lei1() # 使用子类1创造一个对象wo
wo.fn() # 输出结果是我的身高有180
ni = Zi_lei2() # 使用子类2创造一个对象ni
ni.fn() # 输出结果是我叫小明
多层继承
与继承子类方法一样。
class Lei1(object):
def info(self):
print('我是父类')
class Lei2(Lei1):
def fo(self):
print('我是子类')
class Lei3(Lei2):
pass
da_chui=Lei3()
da_chui.info()
da_chui.fo()
'''
我是父类
我是子类
'''
上面例子中,Lei3中没有写如何函数,只继承了Lei2中的函数,而Lei2又继承了Lei1的函数,则就使得Lei3有了Lei1和Lei2的函数,这就是多层继承。
一次性调用父类的同名属性和方法
使用super方法。该方法可以快速的调用父类的方法,格式有两种:
1、super(当前类名,self).函数() 2、super().函数()
在这里推荐使用第2种方法比较简洁。
class Lei1(object): # 父类1
def __init__(self):
self.name = '我是父类1'
def info(self):
print(self.name)
class Lei2(object): # 父类2
def __init__(self):
self.name = '我是父类2'
def fn(self):
print(self.name)
class Zi_lei(Lei1, Lei2): # 子类
def __init__(self): # 子类的属性
self.name = '我是子类!'
def han_shu(self):
print(self.name) # 打印子类的属性
def oll_Lei(self):
#推荐方法:
super().__init__()
super().info()
super().__init__()
super().fn()
#另一个方法,效果一样
# super(Zi_lei, self).__init__()
# super(Zi_lei, self).info()
# super(Zi_lei,self).__init__()
# super(Zi_lei, self).fn()
da_chui = Zi_lei() # 创建对象为大锤
da_chui.han_shu()
da_chui.oll_Lei()
'''
我是子类!
我是父类1
我是父类1
'''
常规调用父类中的方法是在子类中自定义一个函数,在函数内部调用父类中的属性和方法,但是这样的方法比较的长,如果类名发生改变,则都需要进行修改。而super方法不需要。但是这个方法有局限性,上面例子就是一个对多继承的调用,在调用时代码十分的多,所有只适用于单继承。并且它的调用顺序是遵循__mro__类属性的顺序。
__mro__
使用__mro__方法可以打印当前对象的调用顺序,使用上面super方法的例子中的代码,打印结果为:
print(Zi_lei.__mro__)
#(<class '__main__.Zi_lei'>, <class '__main__.Lei1'>, <class '__main__.Lei2'>, <class 'object'>)
封装
将属性和方法写的类的里面的这个操作叫做封装。封装可以为属性和方法添加私有权限。有私有权限的属性和方法是不能继承的。
私有属性和方法
在属性名和方法名前面加__表示对这个属性或者方法添加私有权限。是不能继承的
class Lei1(object):
def __init__(self):
self.name = '小明'
self.__money = 200 # 私有属性
def info(self):
print('我是父类')
def __fn(self): # 私有方法
print(self.__money)
class Lei2(Lei1):
pass
da_chui = Lei2() # 创建对象
print(da_chui.name) # 输出结果为小明
da_chui.info() # 输出结果为我是父类
print(da_chui.__money)
da_chui.__fn()
# 运行后会报错
运行后会报'Lei2' object has no attribute '__money'。表示'Lei2' 对象没有属性 '__money'。
修改、获取私有属性
修改和获取需要定义一个共有函数,在该函数中进行对私有属性的操作。一般定义公有方法get_函数名用来获取私有属性,定义公有方法set_函数名用来修改私有属性值。这只是一个建议,不遵守并不会进行报错。
class Lei1(object):
def __init__(self):
self.name = '小明'
self.__money = 200 # 私有属性
def info(self):
print('我是父类')
def __fn(self): # 私有方法
print(self.__money)
def get_money(self): #获取私有属性
print(self.__money)
def set_money(self): #修改私有属性
self.__money=20
class Lei2(Lei1):
pass
da_chui=Lei2()
da_chui.get_money() #输出结果为200
da_chui.set_money() #修改私有属性
da_chui.get_money() #输出结果为20
多态
指通过传入不同的对象,可以产生不同的结果,是在继承的原基础上产生的。
class Lei1(object): # 表示在做什么
def info(self):
print('我在吃饭')
class Lei2(Lei1): # 表示在做什么
def info(self):
print('我在睡觉')
class Lei3(Lei1): # 表示在做什么
def info(self):
print('我在玩手机')
class Wo(object): # 表示r
def fn(self, lei): # 传入不同的对象,执行不同的函数
lei.info()
shi_qing1=Lei1() #创建事情1对象
shi_qing2=Lei2() #创建事情2对象
shi_qing3=Lei3() #创建事情3对象
da_chui = Wo() #使用人创建一个叫大锤的对象
da_chui.fn(shi_qing1) # 将事情1传入
da_chui.fn(shi_qing2) # 将事情2传入
da_chui.fn(shi_qing3) # 将事情3传入
'''
我在吃饭
我在睡觉
我在玩手机
'''
上面的例子中,通过传入不同的对象,得到了不同的结果,这个就是多态。
注意:下面的例子中,虽然与上面的例子输出结果一样,但是这不是多态,多态是在继承的继承上的。
class Lei1(object):
def info(self):
print('我在吃饭')
class Lei2(object):
def info(self):
print('我在睡觉')
class Lei3(object):
def info(self):
print('我在玩手机')
class Wo(object):
def fn(self,lei): #形式参数
lei.info()
da_chui=Wo()
da_chui.fn(Lei1()) #将Lei1()传入
da_chui.fn(Lei2()) #将Lei2()传入
da_chui.fn(Lei3()) #将Lei3()传入
'''
我在吃饭
我在睡觉
我在玩手机
'''