python之初识面向对象
面向过程,函数式,面向对象三者之间的区别:
- 面向过程:根据业务逻辑从上到下写垒代码
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强…
1.类的基本结构
class A:
name = 'alex' # 静态属性,静态变量,静态字段。
def func1(self): # 函数,动态属性,方法。
pass
2.函数与面向对象的区别
1,函数封装一个功能,而面向对象封装多个相关的功能。
2,面向对象抽象,它是一种思想,站在上帝的角度去理解他。
3,程序可扩展,对象都是一个个独立的。耦合性,差异性。
3.类与对象的区别
类:具有相同属性或者功能的一类实物。
对象:对象是类的具体体现。
类的申明:
class 类名: #class申明一个类,类名首字母大写
类属性
类方法
实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
1 class Person: #定义一个人类
2 role = 'person' #人的角色属性都是人
3 def __init__(self,name):
4 self.name = name # 每一个角色都有自己的昵称;
5
6 def walk(self): #人都可以走路,也就是有一个走路方法
7 print("person is walking...")
8
9
10 print(Person.role) #查看人的role属性
11 print(Person.walk) #引用人的走路方法,注意,这里不是在调用
实例化的过程就是类——>对象的过程
原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。
语法:对象名 = 类名(参数)
实例化过程内部进行了三个阶段:
1,在内存中开辟了一个对象空间
2,自动执行类中的__init__方法,并且将对象空间自动传给self参数,其他参数手动传入。
3, 执行__init__方法 给对象空间封装相应的属性。
self
self:在实例化时自动将对象/实例本身传给__init__的第一个参数
类属性的补充
1 一:我们定义的类的属性到底存到哪里了?有两种方式查看
2 dir(类名):查出的是一个名字列表
3 类名.__dict__:查出的是一个字典,key为属性名,value为属性值
4
5 二:特殊的类属性
6 类名.__name__# 类的名字(字符串)
7 类名.__doc__# 类的文档字符串
8 类名.__base__# 类的第一个父类
9 类名.__bases__# 类所有父类构成的元组
10 类名.__dict__# 类的字典属性
11 类名.__module__# 类定义所在的模块
12 类名.__class__# 实例对应的类(仅新式类中)
1 print(Person.animal) # 查
2 print(Person.language)
3 Person.name = 'alex' # 增
4 print(Person.name)
5 Person.animal = '低级动物' # 改
6 del Person.walk_way # 删
7 print(Person.__dict__)
查看,(增删改)类中某个,某些属性 用万能的点 .
类名称空间与对象的名称空间
创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性
而类有两种属性:静态属性和动态属性
- 静态属性就是直接在类中定义的变量
- 动态属性就是定义在类中的方法
其中类的数据属性是共享给所有对象的,而类的动态属性是绑定到所有对象的。
创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性
对象操作对象空间
对象查看对象空间所有的属性 __dict__
print(obj.__dict__)
对象操作对象的某个属性 增删改查 万能的点.
对象操作类空间的属性 只能查
对象能调用类中的属性与方法而且只是调用不能改变
对象与对象之间可不可互相调用?
同一个类实例化出来的对象之间是不能互相访问的。
不同类实例化的对象有可能互相访问。
给对象封装属性:__init__ 任意位置。
组合: 给一个类的对象封装一个属性,这个属性是另一个类的对象。
组合的意义:让类的对象与另一个类的对象产生关系,类与类之间产生关系
1 class Gamerole:
2 def __init__(self,nickname,ad,hp):
3 self.nickname = nickname
4 self.ad = ad
5 self.hp = hp
6
7 def attack(self,role):
8 role.hp = role.hp - self.ad
9 print('%s攻击%s,%s掉了%s血,还剩%s血' %\
10 (self.nickname,role.nickname,role.nickname,self.ad,role.hp))
11
12 def equip_weaon(self,w): # 给人物封装了一个武器属性,这个属性值是Weapon类的一个对象
13 self.weapon = w # 组合
14
15
16 class Weapon:
17 def __init__(self,name,ad):
18 self.name = name
19 self.ad = ad
20 def fight(self,role1,role2):
21 role2.hp = role2.hp - self.ad
22 print('%s 用 %s 攻击了%s, %s掉了%s血,还剩%s血'\
23 %(role1.nickname,self.name,role2.nickname,role2.nickname,self.ad,role2.hp))
24
25 p1 = Gamerole('盖伦',20,500)
26 p2 = Gamerole('剑豪',100,200)
27 p1.attack(p2)
28 print(p2.hp)
29 w1.fight(p1,p2) # 这样不好,动作的发起者应该是人而不是武器
30 w1 = Weapon('大宝剑',30)
31 w2 = Weapon('武士刀',80)
32 print(w1)
33 p1.equip_weaon(w1)
34 print(p1.weapon) #其实 他就是w1
35 p1.weapon.fight(p1,p2)
36 让剑豪利用武士刀给盖伦一刀
37 p2.equip_weaon(w2)
38 p2.weapon.fight(p2,p1)
面向对象三大特性(封装,继承,多态)
1.封装
封装的定义
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
封装的优点:
1. 将变量隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
封装原则
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
封装的分类:广义封装和狭义封装
广义上的封装(把属性函数都放到类里)
class 类名:
def 方法1(self):pass
是为了只有这个类的对象才能使用定义在类中的方法
狭义上的封装(定义私有成员,私有变量和私有方法)
把一个名字藏在类中(采用变量名前加双下划线方式)
class Goods:
__discount = 0 # 私有的静态变量
print(__discount)
print(Goods.__discount) # 在类的外部不能引用私有的静态变量
类中的静态变量和方法名在程序加载的过程中就已经执行完了,不需要等待调用
在这个类加载完成之前,Goods这个名字还没有出现在内存空间中
私有的静态属性可以在类的内部使用,用来隐藏某个变量的值
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
1 class A: 2 __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N 3 def __init__(self): 4 self.__X=10 #变形为self._A__X 5 def __foo(self): #变形为_A__foo 6 print('from A') 7 def bar(self): 8 self.__foo() #只有在类内部才可以通过__foo的形式访问到.
A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种自动变形的特点:
1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,
即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,
知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
1 class Goods: 2 __discount = 0 # 私有的静态变量 3 # 变形 : _类名__私有变量 4 5 print(Goods.__dict__) 6 print(Goods._Goods__discount) # 编程规范的角度上出发 我们不能在类的外部使用私有的变量 7 8 class Srudent: 9 def __init__(self,name,age): 10 self.__name = name 11 self.age = age 12 def name(self): 13 return self.__name 14 zhuge = Srudent('诸葛',20) 15 print(zhuge.name()) 16 print(zhuge.age) 17 zhuge.age = 'aaaa' 18 print(zhuge.age)
1 class Goods: 2 __discount = 0.7 # 私有的静态变量 3 def __init__(self,name,price): 4 self.name = name 5 self.__price = price 6 7 def price(self): 8 return self.__price * Goods.__discount 9 10 def change_price(self,new_price): 11 if type(new_price) is int: 12 self.__price = new_price 13 else: 14 print('本次价格修改不成功') 15 16 apple = Goods('APPLE',5) 17 print(apple.price()) 18 apple.change_price('aaaa') 19 print(apple.price())
1 class User: 2 def __init__(self,username,password): 3 self.usr = username 4 self.__pwd = password 5 self.pwd = self.__getpwd() 6 7 def __getpwd(self): 8 return hash(self.__pwd) 9 10 obj = User('alex','alex3714') 11 print(obj.usr,obj.pwd)
类中的私有成员:
私有的静态属性
私有的对象属性
私有的方法
我为什么要定义一个私有变量呢:
我不想让你看到这个值
我不想让你修改这个值
我想让你在修改这个值得时候有一些限制
有些方法或者属性不希望被子类继承
私有变量能不能在外部被定义???
1 class A : 2 __country = 'China' # 在类的内部会发生变形 3 print(__country) # '_A__country' 4 print(A.__dict__) 5 A.__Language = 'Chinese' 6 print(A.__dict__)
私有变量能不能被继承???
1 class A: 2 __country = 'China' 3 def __init__(self,name): 4 self.__name = name # '_A__name' 5 6 class B(A): 7 # print(__country) 8 # NameError: name '_B__country' is not defined 9 def get_name(self): 10 return self.__name # '_B__name' 11 12 b = B('alex') 13 print(b.__dict__)
property是一个装饰器函数
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,
根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
所有的装饰器函数都怎么用? 在函数、方法、类的上面一行直接@装饰器的名字
装饰器的分类:
装饰函数
装饰方法 : property
装饰类
1 class Student: 2 def __init__(self,name,age): 3 self.__name = name 4 self.age = age 5 @property # 将一个方法伪装成一个属性 6 def name(self): 7 return self.__name 8 zhuge = Student('诸葛',20) 9 print(zhuge.name) 10 11 from math import pi 12 class Circle: 13 def __init__(self,r): 14 self.r = r 15 @property 16 def area(self): 17 return self.r ** 2 * pi 18 @property 19 def perimeter(self): 20 return 2 * self.r * pi 21 22 c1 = Circle(10) 23 print(c1.area) 24 print(c1.perimeter) 25 c1.r = 5 26 print(c1.area) 27 print(c1.perimeter)
一个方法被伪装成属性之后
应该可以执行一个属性的增删改查操作
那么增加和修改 就对应这被setter装饰的方法 :这个方法又一个必传的参数new,表示赋值的时候等号后面的值
删除一个属性 对应着 被deleter装饰的方法,这个方法并不能在执行的时候真的删除这个属性,而是你在代码中执行什么就有什么效果
一个静态属性property本质就是实现了get,set,delete三种方法
1 class Foo: 2 @property 3 def AAA(self): 4 print('get的时候运行我啊') 5 6 @AAA.setter 7 def AAA(self,new): 8 print('set的时候运行我啊') 9 10 @AAA.deleter 11 def AAA(self): 12 print('delete的时候运行我啊') 13 14 #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter 15 f1=Foo() 16 f1.AAA 17 f1.AAA='aaa' 18 del f1.AAA
property的应用
1 class Goods: 2 3 def __init__(self): 4 # 原价 5 self.original_price = 100 6 # 折扣 7 self.discount = 0.8 8 9 @property 10 def price(self): 11 # 实际价格 = 原价 * 折扣 12 new_price = self.original_price * self.discount 13 return new_price 14 15 @price.setter 16 def price(self, value): 17 self.original_price = value 18 19 @price.deleter 20 def price(self): 21 del self.original_price 22 23 24 obj = Goods() 25 obj.price # 获取商品价格 26 obj.price = 200 # 修改商品原价 27 print(obj.price) 28 del obj.price # 删除商品原价
2.继承
# 继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类
# 父类又可称为基类或超类,新建的类称为派生类或子类
查看继承方法
1 # subclass.__bases__
2 #如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。
3 #>>> ParentClass1.__bases__
4 (<class 'object'>,)
5 >>> ParentClass2.__bases__
6 (<class 'object'>,)继承与抽象(先抽象后继承)
抽象即抽取类似或者说比较像的部分
继承:是基于抽象的结果,通过编程语言去实现它,
肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
继承的分类:
1.单继承
语法 : 子类名(父类名)
继承与重用
# 子类可以使用父类中的名字(变量和方法)
继承与派生
# 子类在父类的基础上又新创建了自己需要的方法和属性
父类有的子类没有 - 子类对象直接调用 就会直接执行父类的方法
父类有的子类也有 - 子类对象调用 直接执行子类中的方法
想在子类中使用父类的名字 : 父类名、super()去调用
当子类当中有要被调用的方法的时候,子类的对象会直接选择子类中的方法、变量,父类中的方法不会被自动执行
如果我们既想要执行子类的方法,也想要执行父类的方法,那么需要在子类的方法中调用父类的方法:
1.父类名.方法名(self,...)
2.super().方法名(...)
抽象类
抽象类是一个规范,它基本不会实现什么具体的功能,抽象类是不能被实例化
要想写一个抽象类
from abc import ABCMeta,abstractmethod
在这个类创建的时候指定 metaclass = ABCMeta
在你希望子类实现的方法上加上一个 @abstractmethod装饰器
使用抽象类
继承这个类
在创建新类时,必须实现这个类中被@abstractmethod装饰器装饰的方法
如果没有写入这个方法则会报错
TypeError: Can't instantiate abstract class qq with abstract methods pay, shouqian
1 from abc import ABCMeta,abstractmethod 2 3 class Payment(metaclass=ABCMeta): # 模板的功能 4 @abstractmethod # abstractmethod是一个装饰器,装饰器怎么用?放在函数/类的上一行 5 def pay(self):pass 6 7 @abstractmethod 8 def shouqian(self):pass 9 10 class Alipay(Payment): 11 def pay(self,money): 12 print('使用支付宝支付了%s元'%money) 13 14 class Wechatpay(Payment): 15 def pay(self,money): 16 print('使用微信支付了%s元'%money) 17 18 class ApplePay(Payment): 19 def pay(self,money): 20 print('使用applepay支付了%s元' % money) 21 22 def pay(obj,money): 23 obj.pay(money)
2.多继承
不是所有的语言都支持多继承 java
c++ 支持多继承
1 class Animal: 2 def __init__(self,name): 3 self.name = name 4 def talk(self): 5 print('%s说话了'%self.name) 6 7 def swim(self): 8 print('%s在游泳'%self.name) 9 10 def fly(self): 11 print('%s在飞'%self.name) 12 13 def walk(self): 14 print('%s在走路'%self.name) 15 class Animal: 16 def __init__(self,name): 17 self.name = name 18 19 class FlyAnimal(Animal): 20 def fly(self): 21 print('%s在飞' % self.name) 22 class WalkAnimal(Animal): 23 def walk(self): 24 print('%s在走路'%self.name) 25 class SwimAnimal(Animal): 26 def swim(self): 27 print('%s在游泳'%self.name) 28 29 class Tiger(SwimAnimal,WalkAnimal): 30 pass 31 32 class Swan(SwimAnimal,WalkAnimal,FlyAnimal): 33 pass 34 35 class Parrot(FlyAnimal,WalkAnimal): 36 def talk(self): 37 print('%s说话了'%self.name) 38 39 swan = Swan('天鹅') 40 swan.fly() 41 swan.walk()
继承原理
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,
这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
>>> F.mro() #等同于F.__mro__
[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, \
<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
这个MRO列表的构造是通过一个C3线性化算法来实现的
有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
继承的作用:
减少代码的重用
提高代码可读性
规范编程模式
抽象中的几个概念:
抽象:抽象即抽取类似或者说比较像的部分。是一个从具题到抽象的过程。
继承:子类继承了父类的方法和属性
派生:子类在父类方法和属性的基础上产生了新的方法和属性
多继承问题
在继承抽象类的过程中,我们应该尽量避免多继承;
而在继承接口的时候,我们反而鼓励你来多继承接口
钻石继承
新式类:广度优先
经典类:深度优先
接口
# java c# 不允许多继承
# 接口 Interface 接口可以被多继承
# Interface FlyAnimal: # 规范继承我的类必须实现这个方法
# def fly():pass #java不允许写任何代码
# Interface WalkAnimal:
#def walk():pass
# Interface SwimAnimal:
# def swim():pass
# class Tiger(WalkAnimal,SwimAnimal): 继承了一个规范
# def walk():代码
# def swim():代码
新式类和经典类
新式类中
所有的多继承关系寻找方法的顺序 - 遵循广度优先算法
继承object
mro方法
super : super不是单纯的找父类,而是遵循mro顺序的
新式类
在python3.x版本中 所有的类都是新式类
所有的新式类都有一个默认的父类 : object
#class Person1:pass
# class Person2():pass
# class Person3(object):pass
# # p = Person2() # __init__ 初始化方法
# print(Person1.__bases__)
# print(Person2.__bases__)
# print(Person3.__bases__)
python 2.7
经典类 和 新式类 并存
class Student:pass # 经典类
class Student(object):pass
继承了object的类就是新式类
在py3中所有的类都是新式类
在py2中既有新式类又有经典类
多继承的顺序 在新式类和经典类之间的区别
class A: def func(self): print('A') class B(A): pass # def func(self): # print('B') class C(A): pass # def func(self): # print('C') class D(B,C): pass # def func(self): # print('D') print(D.mro()) d = D() d.func()
新式类中
所有的多继承关系寻找方法的顺序 - 遵循广度优先算法
继承object
mro方法
super : super不是单纯的找父类,而是遵循mro顺序的
经典类
python2.x
不主动继承object
经典类在找父类中方法的过程中 遵循 —— 深度优先
不提供mro方法和super
1 class A: 2 def func(self): 3 print('A') 4 5 class B(A): 6 def func(self): 7 super().func() 8 print('B') 9 10 class C(A): 11 def func(self): 12 super().func() 13 print('C') 14 15 class D(B,C): 16 def func(self): 17 super().func() 18 print('D') 19 20 D().func() 21 B().func()
经典类
python2.x
不主动继承object
经典类在找父类中方法的过程中 遵循 —— 深度优先
不提供mro方法和super
3.多态
什么是多态呢?
一个类表现出的多种状态 : 通过继承来实现的
在java中的表现 : 在一个函数中需要给参数指定数据类型,如果这个地方可以接收两个以上类型的参数,
那么这些类型应该有一个父类,这个父类是所有子类对象的类型
java中运用多态来解决传参数的时候 数据类型的规范问题
def func(Cat mao):pass
def func(Dog gou):pass
def func(Animal gou|mao):pass
在python中:函数的参数不需要指定数据类型,所以我们也不需要通过继承的形式来统一一组类的类型,
换句话说 所有的对象其实都是object类型,所以在python当中其实处处是多态
鸭子类型
def len(obj)
len() # str list tuple dict set range(3)
print() # 所有的对象都是鸭子类型
不是明确的通过继承实现的多态
而是通过一个模糊的概念来判断这个函数能不能接受这个类型的参数
类与对象调用的方法的区别
类调用自己的方法类型是FunctionType,除调用类方法类型是MethodType
函数调用类方法是MethodType,除调用静态方法时是FunctionType
类方法(只使用类中的资源,且这个资源可以直接用类名引用的使用,那这个方法应该被改为一个类方法)
类中包含的内容
1.静态属性 类 所有的对象都统一拥有的属性
2.类方法 类 如果这个方法涉及到操作静态属性、类方法、静态方法 cls 表示类
3.静态方法 类 普通方法,不使用类中的命名空间也不使用对象的命名空间 : 一个普通的函数 没有默认参数
4.方法 对象 self 表示对象
5.property方法 对象 slef 表示对象
检测对象与类之间的关系(isinstance(object,class))
判断这个对象是否是这个类、这个类的子类的对象
1 class A: 2 pass 3 class B(A): 4 pass 5 a = A() 6 b = B() 7 print(isinstance(a,A)) 8 print(isinstance(b,A)) 9 print(isinstance(a,B))
检测类与类之间的关系(issubclass(son_class,father_class))
判断一个类是否是另一个类的子类
1 class A: 2 pass 3 class B(A): 4 pass 5 print(issubclass(A,B)) 6 print(issubclass(B,A))
classmethod,staticmethod方法
classmethod
在类中定义一个类方法 、是一个装饰器
classmethod作用
如果你的整个方法中都没有用到对象命名空间中的名字,且你用到了类的命名空间中的名字(普通方法和property方法除外)
类方法的默认参数 : cls 值得是调用这个方法的类
类方法的调用方式 : 通过类名调用,本质是方法
staticmethod
将一个普通的函数放到类中来就给这个函数加上一个@staticmethod装饰器
这个函数就不需要传默认的参数:self,cls
静态方法的调用方式 : 通过类名调用,本质还是函数
1 class Goods: 2 __discount = 0.8 # 静态属性 3 def __init__(self,price): 4 self.__price = price # 对象属性 5 self.name = 'apple' 6 7 @property 8 def price(self): 9 print(self) 10 return self.__price * Goods.__discount 11 12 @classmethod 13 def change_discount(cls,new): # 类方法 14 cls.__discount = new 15 16 class Foo: 17 @classmethod 18 def class_method(cls):pass 19 20 @staticmethod #静态方法 21 def static_method():pass 22 23 def simple_method(self): 24 pass 25 26 from types import MethodType,FunctionType 27 obj = Foo() 28 print(isinstance(obj.class_method, MethodType)) 29 print(isinstance(obj.class_method, FunctionType)) 30 print(isinstance(obj.static_method, MethodType)) 31 print(isinstance(obj.static_method, FunctionType)) 32 print(isinstance(obj.simple_method, MethodType)) 33 print(isinstance(Foo.simple_method, FunctionType)) 34 print(isinstance(Foo.static_method, FunctionType)) 35 print(isinstance(Foo.class_method, FunctionType)) 36 print(isinstance(Foo.class_method, MethodType))