面向对象编程
类的概念 : 具有相同属性和技能的一类事物
人类 抽象
对象 : 就是对一个类的具体的描述
具体的人 具体
使用面向对象的好处:
使得代码之间的角色关系更加明确
增强了代码的可扩展性
规范了对象的属性和技能
面向对象的特点:结局的不确定性
1 def Person(name,sex,hp,ad):
2 # 人模子
3 self = {'name': name, 'sex':sex, 'hp': hp, 'ad': ad}
4 def attack(dog): # 闭包
5 # 人攻击狗
6 print('%s攻击%s' % (self['name'], dog['name']))
7 # 狗掉血,狗的血量-人的攻击力
8 dog['hp'] -= self['ad']
9 self['attack'] = attack
10 return self
11
12 def Dog(name,kind,hp,ad):
13 # 狗模子
14 self = {'name': name, 'kind':kind, 'hp': hp, 'ad': ad}
15 def bite(person):
16 print('%s咬了%s' % (self['name'], person['name']))
17 # 人掉血,人的血量-狗的攻击力
18 person['hp'] -= self['ad']
19 if person['hp'] <= 0: print('game over,%s win' % self['name'])
20 def bite2():pass
21 self['bite'] = bite
22 self['bite2'] = bite2
23 return self
24
25 # 人 规范了属性的个数 简化了创造任务的代码
26 alex = Person('a_sb','不详',1,5)
27 boss_jin =Person('金老板','女',2,50)
28
29 # 狗
30 chen = Dog('旺财','teddy',50,20)
31 alex['attack'](chen)
32 print(chen['hp'])
在讲语法规则之前我们先说下类的基础的东西
静态属性
class Person: #类名
role = 'person' # 静态属性
def f1(self): # 动态属性 方法(函数) 默认带一个参数self
print(1234567)
查看静态变量的第一种方法:
print(Person.__dict__) 把类中的所有的变量以字典的方式打印出来
print(Person.__dict__['role']) 把role对应的值打印出来
查看静态变量的第二种方法:
print(Person.role)
Person.role = 123 改变了静态属性的值
Person.__dict__['role'] = 456 报错
del Persom.sole 删除静态属性
动态属性和一些语法规则
引用动态变量
# 1.类名.方法名 查看这个方法的内存地址
# 1.类名.方法名(实参) 调用了这个方法,必须传一个实参,这个实参传给了self
# 创造一个对象 - 实例化
# 产生一个实例(对象)的过程
# 对象 = 类名()
# alex = Person() # 创造一个对象
# alex 是对象、实例
# Person是类
# 对象 = 类名()
# 实例化的过程:
# 1.创造一个实例,将会作为一个实际参数 # python
# 2.自动触发一个__init__的方法,并且把实例以参数的形式传递给__init__方法中的self形参
# 3.执行完__init__方法之后,会将self自动返回给alex
# __init__方法 :初始化方法,给一个对象添加一些基础属性的方法,一般情况下是针对self的赋值
# 对象
# 在类的内部 self是本类的一个对象
# 在类的外部,每一个对象都对应着一个名字,这个对象指向一个对象的内存空间
# 属性的调用:
# 对象名.属性名 第一种调用方法
# 对象名.__dict__['属性名'] 第二种调用方法
# 方法的调用 :
# 类名.方法名(对象名) # 那么方法中的self参数就指向这个对象
# 对象名.方法名() # 这样写 相当于 方法中的self参数直接指向这个对象
class Person:
s = 'aaaa'
def __init__(self,name,sex,hp,ad):
self.name = name
self.sex = sex
self.hp = hp
self.ad = ad
def atack(self):
print('%s攻击%s' % (self.name,gou.name))
class Dog:
def __init__(self,name,sex,hp,ad):
self.name = name
self.sex = sex
self.hp = hp
self.ad = ad
def bite(self):
print('%s咬了%s' % (self.name,alex.name))
print(Person.s)
alex = Person('alex','male',78,23)
gou = Dog('wangcai','female',4,56)
print(alex.__dict__)
print(alex.name)
alex.name = 'sb'
print(alex.name)
alex.atack()
gou.bite()
Person.atack(alex)
Dog.bite(gou)
#############
aaaa
{'name': 'alex', 'sex': 'male', 'hp': 78, 'ad': 23}
alex
sb
sb攻击wangcai
wangcai咬了sb
sb攻击wangcai
wangcai咬了sb
面向对象中的一些作用域问题
在创建一个类时,类里面的静态变量和方法 公用一个内存空间
每个对象里的 所有对象属性公用一个内存空间 内存空间里面包含一个类对象指针 当在自己的空间找不到对象属性时 就通过类对象指针去类的内存空间去找
对类静态属性的更改 应该使用 类名.静态属性 = 新的值
class Person:
role = 'person' # 静态属性
def __init__(self,name,sex,hp,ad): # 方法
self.name = name # 对象属性 属性
self.sex = sex
self.hp = hp
self.ad = ad
self.attack = 'hahaha'
def attack(self):
print('%s发起了一次攻击'%self.name)
alex = Person('a_sb','不详',1,5)
boss_jin = Person('金老板','女',50,20)
print(alex.role)
print(boss_jin.role)
alex.role = 'dog' #这句代码是在自己的空间内创建了一个对象属性 role = ‘dog’
print(alex.role) #这句代码在找role这个对象属性时 先找自己的空间
print(boss_jin.role) # 结果为'person' 这个在自己的空间找不到所以去找了类的空间里的role
组合
组合:一个类的对象作为另一个类的对象的属性 表示一种什么有什么的关系
能够组合必须是两个对象有可以互相关联的因素。
from math import pi
class Circle:
def __init__(self,r):
self.r = r
def s(self):
return pi*self.r**2
def lenth(self):
return 2*pi*self.r
class Ring:
def __init__(self,r,R):
self.r = Circle(r) 组合
self.R = Circle(R) 组合
def s(self):
return abs(self.r.s() - self.R.s())
def lenth(self):
return self.r.lenth() + self.R.lenth()
a = Ring(3,5)
print(a.s(),a.lenth())
继承
父类 :基类 超类
子类 :派生类
继承的特点:什么里面是什么的关系 继承是当几个类的对象都有共同的属性的时候可以用继承这样可以不用输入重复的代码。
含有继承关系的子类在找东西的时候先找自己内存空间 再找自己类的空间 再找父类空间
class Animal:
role = 'Animal'
def __init__(self,name,hp,ad):
self.name = name # 对象属性 属性
self.hp = hp #血量
self.ad = ad #攻击力
def eat(self):
print('%s吃药回血了'%self.name)
class Person(Animal):
r = 'Person'
def attack(self,dog): # 派生方法
print("%s攻击了%s"%(self.name,dog.name))
def eat2(self):
print('执行了Person类的eat方法')
self.money = 100
self.money -= 10
self.hp += 10
class Dog(Animal):
def bite(self,person): # 派生方法
print("%s咬了%s" % (self.name, person.name))
alex = Person('alex',10,5)
dog = Dog('teddy',100,20)
alex.eat2()
alex.eat()
dog.eat()
alex.eat = 'aaa'
print(alex.eat()) # 报错
###############
执行了Person类的eat方法
alex吃药回血了
teddy吃药回血了
派生
在继承父类属性的时候,假如几个子类的90%的属性都相似,而只有剩下的10%不一样,这时候就需要用到派生属性的方法
class Animal:
def __init__(self,kind,sex):
self.kind = kind
self.sex = sex
def func1(self):
return '吃'
def func2(self):
print( '喝')
class Dog(Animal):
def __init__(self,kind,sex,kg):
# Animal.__init__(self,kind,sex)
super().__init__(kind,sex) 在单继承中supper负责找到父类的,不需要传self
self.height = kg 派生属性
def func3(self):
print('看门')
def func1(self):
print('抓老鼠')
class Cat(Animal):
def __init__(self,kind,sex,age):
Animal.__init__(self,kind,sex)
self.age = age
def func4(self):
print('上树')
super().func2() 找父类的中的方法
dog = Dog('二哈','male','30kg')
cat = Cat('加菲猫','female',3)
print(dog.__dict__)
print(cat.__dict__)
cat.func4()
########
{'kind': '二哈', 'sex': 'male', 'height': '30kg'}
{'kind': '加菲猫', 'sex': 'female', 'age': 3}
上树
喝
砖石继承问题
在python3x 中所有的类都是新式类(所有的类都默认含有object)所以内部的遍历顺序都是以广度优先的顺序来遍历,当用super的时候请看下面的例子
class A: def func(self): print('A') class B(A): pass def func(self): super().func() print('B') class C(A): pass def func(self): super().func() print('C') class D(B): pass def func(self): super().func() print('D') class E(B,C): pass def func(self): super().func() print('E') class F(D,E): pass def func(self): super().func() print('F') f = F() f.func()
print(mro(f)) 查找他的上一个父类
这段代码super查找父类中的方法查找顺序就如图所示 遵循广度优先的顺序 并且每次找遍历节点只遍历一次
在python2x 中就不一样了python2中的砖石继承分为经典类和新式类
经典类(也就是不含有object):在经典类中在子类寻找父类的时候遵循深度优先 没有mro方法 也没有super方法
新式类(就是含有object):在新式类中在子类寻找父类的时候遵循广度优先 有mro方法 也有super方法但是super方法要有传参super(子类名称,子类对象)
这个例子为经典类的遍历方法
class A:
# def func(self):
# print('A')
pass
class B(A):
pass
# def func(self):
# # super().func()
# print('B')
class C(A):
pass
def func(self):
# super().func()
print('C')
class D(B):
pass
# def func(self):
# # super().func()
# print('D')
class E(B,C):
pass
# def func(self):
# # super().func()
# print('E')
class F(D,E):
pass
# def func(self):
# # # super().func()
# print('F')
f = F()
f.func()
抽象类和接口类
说这个问题之前我们要先来聊聊编程届的两本黑皮书
《设计模式》 该书中记载了很多衍生下来的很多程序的设计模式 比如JAVA语言的单继承的设计模式,因为在java语言中没有多继承。
《算法导论》 记载了很多经典的算法 及时在今天没有用了的一些算法 都记录了下来
我们继续来说抽象类和接口类 接口类和抽象类都只是一种编程规范 只是为了规范编程而已,并不能让你的程序变得这么样,如果这个程序只由我自己一个人来完成 我自己遵守默认的规范那么我就不需要进行这些操作
来我们看一段代码
这段代码中的class A 就是抽象类 抽象类的目的是为了下面的类都继承A类的方法 因为在调用方法的时候用了一个归一化设计 以免其他的程序员在创建类的方法的时候没有创建抽象类的方法
导致归一化设计的函数在调用类的方法的时候出错
from abc import ABCMeta,abstractmethod
class A(metaclass = ABCMeta):
@abstractmethod
def pay(self):pass
class Alipay(A):
def pay(self,money):
print('支付宝支付%s元' % money)
class QQpay(A):
def pay(self,money):
print('用qq支付了%s元' % money)
class WEchat(A):
def pay(self,money):
print('用微信支付%s元' % money)
a = Alipay()
B = QQpay()
c = WEchat()
def pay(a,money): 这里就是归一化设计
a.pay(money)
pay(a,100)
pay(B,200)
pay(c,300)
######
支付宝支付100元
用qq支付了200元
用微信支付300元
我们在来看另一个代码
from abc import ABCMeta,abstractmethod
class FlyAnimal(metaclass=ABCMeta):
@abstractmethod
def fly(self):pass
@abstractmethod
def cal_flying_speed(self):pass
@abstractmethod
def cal_flying_height(self):pass
class WalkAnimal(metaclass=ABCMeta):
@abstractmethod
def walk(self):pass
class SwimAnimal(metaclass=ABCMeta):
@abstractmethod
def walk(self):pass
class Tiger(WalkAnimal,SwimAnimal):
def walk(self):pass
def swim(self):pass
class Monkey:
def walk(self):pass
def climb(self):pass
class Swan(FlyAnimal,WalkAnimal,SwimAnimal):
def swim(self):pass
def walk(self):pass
def fly(self):pass
def cal_flying_speed(self):pass
def cal_flying_height(self):pass
class Parrot(FlyAnimal):
def fly(self):pass
def cal_flying_speed(self):pass
def cal_flying_height(self): pass
上面这段代码中我们可以看到当多个类中有相同也有不同的方法,一个类中包含几个抽象类中的方法,这时候也由于java没有多继承 所以当java遇到这种情况的时候 就需要将我们python中的抽象类 建成接口类 以便于多继承 也就是说java只可以多继承接口类
总结:无论是抽象类也好 还是接口类都是为了规范代码
接口类:是java的概念 所以说接口类只存在java中,是方便java中解决多继承的问题的 同时还有接口隔离的问题(因为当不同的类包含有多种不同的方法时候,就需要建立不同的接口类来隔离)
抽象类 :在python中就是用来规范代码的 在python中遇到不同的类包含不同的方法 又需要规范每个类中的方法名 这时候我们就需要建立不同的抽象类
多态
在说多态之前我们首先来认识下什么是强类型语言什么是弱类型语言
强类型:c c+ c++ c# java 这些强类型的语言都有个共性就是在传参的时候有个特点 只能传一种数据类型的参数
强弱型语言:python
弱类型的语言:php shel 这些语言在数据类型上就没有那么严格
什么是多态我们看下面这个代码
from abc import ABCMeta,abstractmethod
class A(metaclass = ABCMeta):
@abstractmethod
def pay(self):pass
class Alipay(A):
def pay(self,money):
print('支付宝支付%s元' % money)
class QQpay(A):
def pay(self,money):
print('用qq支付了%s元' % money)
class WEchat(A):
def pay(self,money):
print('用微信支付%s元' % money)
a = Alipay() 这里我们看下a 的数据类型为<class '__main__.Alipay'>
B = QQpay() B 的数据类型为 <class'__main__.QQpqy'>
c = WEchat() c 的数据类型为<class'__main__.WEchat'>
def pay(a,money):
a.pay(money)
pay(a,100)
pay(B,200)
pay(c,300)
######
支付宝支付100元
用qq支付了200元
用微信支付300元
上面我们看到了所有的实例化后打变量名的数据类型都为类名 而在我们在调用归一化设计的函数时,传入的第一个位置参数的数据类型时不一样的 在java中是不行的 因为他强语言的特性 所以是绝对不允许每次都传入不同的数据类型 那怎么办呢 ??所以就只有再建立一个父类,让子类都来继承父类,把归一化的函数的传参的数据类型改成父类,传参时 传入子类的数据类型就可以了 ,这也就是java实现多态性的办法。
而这样复杂的问题在python中就不存在了,由于python自带多态性,所以这种问题根本就不存在。
我们深究一下为什么python会自带多态,其实说白了是那些编写python的大佬 达成了一种默契 直接把各种父类子类的问题都解决好了 都写成一样的方法名 所以python才会自带多态
编程原则:
开放封闭原则:对扩展是开放的 对修改是关闭的(比如说装饰器就是对这个原则的执行)
依赖倒置原则:底层依赖上层
接口隔离原则:上面说过了我们据不在叙述了
封装
说道这里咱们再来和java做下对比
public 共有的 在类的内部可以使用 子类可以使用 外部可以使用 python中所有的都可以做到
protect 保护的 在类的内部可以使用 子类可以使用 外部不能使用 Python中没有
private 私有的 在类的内部可以使用 子类不可以使用 外部不能使用 python 中的__名
封装什么叫封装,就是我要把有些属性,和方法,对象的属性封装起来,不愿意在类的外面直接调用修改。
我们来看一个面试题
class D:
def __init__(self):
self.__func()
def __func(self):
print('in D')
class E(D):
def __func(self):
print('in E')
e = E()
###
in D
分析:第一步实例化 然后自动执行了D类中的def__init__(self): self.__func() 找到D类中的私有的方法__func()
父类不想让子类继承自己方法也可以让方法私有化,这样就不能再外面调用父类的方法
如果要调用E中的__func()则要这样写e._E__func() 但是这样虽说可以调用 但是我们绝不可以这样写 这样只是可以做到。
@property 关键字
我们在写类的方法的时候经常会遇到一些方法是动作但是我们得到的结果只是个名词,我们为了规范代码会使用property这个关键字,也就是把方法伪装成属性。
但是需要注意的是,使用property时,这个方法的的参数只能有一个参数(self)
@类名.setter
这种方法是用来对伪装的属性进行赋值时候调用的方法 需要注意的是方法名必须和要伪装的方法名一致 并且赋值的时候调用的方法名也要一致
class A:
def __init__(self,name,discount,price):
self.name = name
self.__discount = discount
self.__price =price
@property
def dis_price(self): 一致dis_price
return self.__discount*self.__price
@dis_price.setter 一致dis_price
def dis_price(self,newdiscount):
self.__discount = newdiscount
apple = A('apple',0.8,5)
print(apple.dis_price)
apple.dis_price = 0.3 一致dis_price
print(apple.dis_price)
####
4
1.5
这里需要注意的是 如果在类中再建立一个和伪装方法名一个属性,则会被将内伪装的方法名覆盖,打印出来的是方法的内存地址
@方法名.deleter 删除方法 这个基本上不用 删除属性
class Person:
def __init__(self,n):
self.__name = n # 私有的属性了
@property # 重要程度 ****
def name(self):
return self.__name
@name.deleter
def name(self):
print('name 被删除了')
@name.deleter # 重要程度*
def name(self):
del self.__name
p = Person('alex')
print(p.name)
del p.name # 只是执行了被@name.deleter装饰的函数
print(p.name)
@classmethod
这个我们举两个例子看下面的代码
class A:
discount__ = 0.8
def __init__(self,name,price):
self.name = name
self.__price = price
@property
def really_price(self):
return self.__price*A.__discount
@classmethod 类方法:可以直接被类调用 不用再实例化 并且只需要传一个类参数 当
def new_discount(cls,newdiscount):
cls.__discount = newdiscount
A.new_discount(0.8) 只需要传一个参数
a = A('a',3)
print(a.really_price)
由这段代码可以看出 当折扣恢复到原来的状态时只需要进行如下操作
A.new_discount(1)
所以当一个方法需要使用类中的静态属性时 而不需要其他操作时 就用类方法
@staticmethod
class Student:
def __init__(self,name):pass
@staticmethod
def login(): # login就是一个类中的静态方法 静态方法没有默认参数 就当成普通的函数使用即可
user = input('user :')
if user == 'alex':
print('success')
else:
print('faild')
Student.login()
当一个方法既不需要用到对象中的属性又不需要用到类中的静态属性 就可以用到静态方法来直接调用类中的方法 不需要实例化