|
三大编程范式:面向过程编程,面向对象编程,函数式编程。
类:把一类事物的相同特征和动作整合到一起就是类。抽象概念。
对象:就是基于类而创建的一个具体事物。
实例化:由类生产对象的过程就是实例化。
比如,类定义了一些特征和动作,那么98K是type 狙击枪,feature杀伤力大,装八倍镜,m24也具有这些特征,除了name不同。就可以将栓狙的共性结合在一起,特征也结合在一起。把他们全部包起来装好。
所以,面向对象设计:将一类具体事物的动作和数据整合在一起。
面向对象编程:用定义类+实例的方式去实现面向对象的设计。
#self其实就是一个字典形式
#声明类和函数相似
#class 类名: (后面可以不要括号,类名首字母大写)
# '类的文档字符串'
# '类体'
#实例化
# d1 = Data() #括号是运行类,实例化,还没传入参数
#实例化的含义:
#属性
#实例是由类产生的,那么实例就应该得到类的属性。
#那么,类一般包含哪些属性呢?
#数据属性:就相当于是特征,比如名字type,这些都是通过变量获得的,所以是变量。
#函数属性:函数,在类中就是方法。
#类和对象都用点来访问自己的属性。 这句话背住。
#print(dir(class)) 查看数据属性名称和函数属性名称,还有内置属性名称。
#print(class.__dir__) #查看类的属性字典,可以看到数据属性和函数属性都存在这个字典中
#在这个字典中,key就是属性名,value就是属性值。
#那么,class.funcname() 本质就是去函数字典中找东西。
#对象相关
#实例化:由类产生对象过程。
#p1 = class(*args) 这个过程是实例化,p1就是实例。记得括号。
#在类中,必须要有一个初始化函数,去定制每一个对象的属性。 __init__。
#(这是啥意思?就是98k,m24是不同的东西,我去向狙击枪这个类实例化,获得98k,m24的对象,名称不同,威力不同,
#那么传入的参数就不同。这些参数需要再类中初始化,你才能调用属性。比如传入98k,然后初始化得到名称,这个名称
#在类的方法中获得它的威力和弹道,这样肯定需要初始化,不初始化类的方法就搞不好。__init__(实例的参数)
#在类中,定义成__init__会自动return成数值属性。
#__init__必须先有一个self属性名字,然后才是传入的参数。比如,self.name 就是给self传入了name属性,这个就是数值属性,
#name属性封装到self中,然后默认return self字典,name变成一个数值属性,name:value。
#实例化过程本质就是调用init. print(p1.__dict__)。所以,实例就是init。
#调用 print(p1.name) 因为name是数值属性,可以通过点调用,name属性对应了一个value,就会显示name对应的值。
#所以,如果调用class.func,如果后面没括号,就是调用这个函数的内存地址,想获得值,就要加括号和参数执行它。
#实例没有函数属性,但是可以从类中调用,因为作用域的关系,它会往init外面去找,找类那一层。
#怎么调?p1.function() 从实例调方法,相当于class.func(p1) 把p1传给了function。
#self就代表实例自己本身。每个方法后面都有self,相当于把实例传给函数。
#类属性 与所属的类对象是绑定的。默认驼峰命名。可以增删改查,方法和字典一样。
#函数属性 默认动词加名词原则,干什么事。
#实例属性的增删改查
#查看实例属性
#print(p1.name) #调实例的数值属性
#print(p1.func) 调函数属性(你会看到这个属性绑定class的信息)
#增加数值属性
#p1.age = 18 #相当于往init的字典里加一个字典项,就是把中括号变成了点
# print(p1.__dir__)
# print(p1.age)
#------------------------------------------
#再来看看能不能在实例属性中加函数属性
# def test():
# print("") 定义一个函数
#p1.test = test
# print(p1.__dir__)
# p1.test()
# 这样不行,因为
1.实例相当于调用了init函数,把实例的变量付给了init,
2. 那么,如果这样增加个函数属性,=init增加了个函数
3.class规定实例的函数属性只能通过class去调用,实例自己做的话不行
4.为什么要这么安排,这样,每个实例的函数属性都是通过唯一一个类获得的。
#不能通过字典方法修改字典属性key值,这样会导致底层的变动,因为后面的函数都是根据这个安排的。之后会有方法。
#可以修改字典的属性数值。和类的一样。
通过实例不能修改类的雷属性,看下面:
class Chinese:
country = 'China'
def __init__(self,name):
self.name = name
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name, ball))
p1 = Chinese('yishu') #初始化实例,首先将init初始化,执行init,如果init中有print,执行print
print(p1.country) #China
p1.country = "Japan"
print(Chinese.country) #输出china
print(p1.country) #输出japan 因为上面修改的是p1实例的数据属性,和类属性没有关系
p2 = p1.play_ball('football') #实例化之后,再调用类的函数,这里可以传入函数参数
print(p2) #步骤别乱
再理解一下下面,为什么往方法属性中传参数要加上self.
country = '------------>>>>'
class Chinese:
country = 'China'
def __init__(self,name):
self.name = name
print('+++',self.country)
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name, ball))
p1 = Chinese('yishu')
print(p1)
输出 +++ ------------>>>> #因为country前面没有点,那么它就是普通的变量,对应上面的变量赋值
<__main__.Chinese object at 0x00000196CC9AA4A8> #内存地址
country = '------------>>>>'
class Chinese:
country = 'China'
def __init__(self,name):
self.name = name
print('+++',self.country)
def play_ball(self,ball):
print('%s 正在打 %s' %(self.name, ball))
p1 = Chinese('yishu')
print(p1)
输出 +++ China 有点,就是在class这个闭区间内。
静态属性 说的就是数据属性
@property的使用 放在函数属性上面,把函数属性变成数值属性,调用函数属性时,不用加上括号。
对函数做了封装操作,把背后逻辑隐藏起来。
类方法 不想和实例捆绑,直接捆绑类的方法,用类调用自己的方法
@classmethod
@classmethod #可以自动传递class的类属性
def tell_info(cls): #括号内接收类名,不用加入实例。
print(cls)
静态方法 @staticmethod 类的工具包 名义上归属类管理,不和类绑定,也不和具体的实例绑定
定义的函数可以用class直接运行函数 class.funcname()
组合 很多小的类组成一个大类
class Sclool():
def __init__(self,name,addr):
self.name = name
self.addr = addr
class Course:
def __init__(self,name,price,period,school):
self.name = name
self.price = price
self.period = period
self.school = school
s1 = Sclool('nannong','beijing')
s2 = Sclool('nannong','nanjing')
c1 = Course('Linux',10,'1h',s1)
print(c1.__dict__)
print(c1.school.name)
print(s1) #c1.school就是s1
类和类之间没有共同点,但是有关联,就可以用组合方式。
面向对象三大特性,继承,多态,封装
类的继承
class ParentClass1:
class ParentClass2:
class SubClass1(ParentClass1) #单继承
class SubClass2(ParentClass1,ParentClass 2) #多继承
到底继承了什么属性呢?
继承了所有属性,如果子类的属性和父类属性重名,相当于先在子类中找,再到父类中找。
那么,继承和组合怎么去用?
当类之间有显著不同,并且较小的类是较大类所需要的组件时,用组合比较好。
当类和类有很多相同功能,提取这些共同的功能做成基类,用继承比较好。
class Manmal():
def __init__(self,name):
self.name = name
def eat_act(self):
print("%s 吃"% self.name)
def aoao(self):
print("叫")
class Dog(Manmal):
def __init__(self,name):
self.name = name
def Dog_wang(self): #子类派生出来的属性
pass
pass
d1 = Dog("狗")
d1.eat_act() #狗吃 dog类中没有eat这个属性,但是可以调用父类的属性
继承同时有2种含义
1.继承基类的方法,并且做出自己的改变或者扩展。第一种意义不是很大。
2.声明某个子类兼容某基类,定义一个接口类,子类继承接口类,并实现借口定义的方法。
接口继承
import abc
class All_file(metaclass=abc.ABCMeta): 父类
@abc.abstractmethod 说明下面的func是抽象的,不用具体实现,但是能限制子类的方法类别。
如果父类有2个方法func1,func2,子类如果没有实现这两个同名的方法func1,func2,或者只定义一个func,子类就不能实例化。
归一化设计:就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
好处:
1.归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。
2. 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,
这个MRO列表就是一个简单的所有基类的线性顺序列表
子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
super调用父类方法 super().__init__()
不用写父类名 不用写self参数
|