python就是:函数式+面向对象
函数前民已经学过,现在学习面向对象
1.创建类
class Person():#创建一个Person类
def foo(self,name,age):#类中的一个方法,方法中必须要有self参数,self参数指的就是对象,其实就是一个内存地址.self的作用之一就是用来区分不同的对象
self.name = name
self.age = age
print(self,self.name,self.age)
obj = Person()#创建一个person对象.对象名是obj
obj.foo('sage',23)#执行foo方法
其实类和对象是通过指针关联起来的,将属性封装到内存中关联起来.
2.构造方法
构造方法就是:obj = Person() 这个语句会有两个作用第一,创建一个对象.第二:执行构造器.
构造器的初始化就是__init__()方法.其中:可以有无参数均可,但是self是一定要有的.其中只要创建对象__init__方法就会自动执行.
例如.
class Person():#创建一个Person类
def __init__(self,name,age,job):
self.name = name
self.age = age
self.job = job
print(self.name,self.age,self.job)
obj = Person('sage',23,'it')#result sage 23 it
其实创建对象就是对于数据的封装.将一些属性放到一个对象里面
3.继承
继承语法
class Father():
def drinking(self):
print('喝酒')
def somking(self):
print('抽烟')
def tangtou(self):
print('烫头')
class son(Father):#son类继承了father类
def piaochang(self):
print('嫖娼')
这个样子呢就是子类继承了父类的基础语法
父类其实也可看做是父亲,比喻来说父亲有三个习惯,抽烟,喝酒,烫头.但是儿子只抽烟不喝酒应该怎么解决呢
class Father():
def drinking(self):
print('喝酒')
def somking(self):
print('抽烟')
def tangtou(self):
print('烫头')
class son(Father):#son类继承了father类
def drinking(self):
print('只抽烟不喝酒')
son = son()
son.drinking()#result:只抽烟不喝酒
son.tangtou()#result:烫头
在上面的内容中涉及到了一个类中的重要方法就是重写,子类重写了与父类相同的一个方法,此时执行子类的这个方法,就会从子类中查找这个方法,而不会去父类中查找这个方法了.
***关于继承方法的查找,假如执行一个方法,python会先在子类中查找,如果找到了就执行,如果没有找到就会去父类查找这个方法.假如父类也没有这个方法,此时会去父类的父类去找.假如父类的父类也没有次方法,会去父类的父类的父类查找,一直找都最上面一层的基类.也就是说一条道走到黑.
在子类方法中的self永远指代的都是子类的对象.
3.1关键字
super:关键字
super关键字的作用:打个比喻来说,父亲抽烟但是父亲那个年代比较穷只能抽旱烟叶,但是儿子呢也抽烟但是生活水平好了不仅抽旱烟叶,还抽中华,玉溪,黄鹤楼等....
class Father():
def smoking(self):
print("父亲抽旱烟叶")
class Son(Father):
def smoking(self):
super(Son,self).smoking()#super(子类名,子类对象).父类中的方法
print('儿子抽中华')
print('儿子抽玉溪')
son = Son()
son.smoking()#result:父亲抽旱烟叶
#儿子抽中华
#儿子抽玉溪
在这里我们可以知道super关键字就是重写的父类的方法,但是子类的这个方法是对父类这个方法的扩充,此时就需要使用super关键字将父类中相同的那一部分先拿过来,然后补充子类独有的方法
与super关键字相同的作用的方法
class father():
def somking(self):
print('旱烟叶')
class son(father):
def somking(self):
father.somking(self)#这个形式中,父类名.方法名(子类对象)
print('中华')
print('玉溪')
son = son()
son.somking()#result:旱烟叶
#中华
#玉溪
一般清空下来说呢,我们常常使用super关键字.
3.3多继承
在其他的语言中都是一个子类只能有一个父类,但是在python中就不是了,有点奇怪哈,一个儿子可以有俩爹.
#多继承
class father1():
def eating(self):
print('father1,eating')
def drinking(selfself):
print('father1.drinking')
class father2():
def eating(self):
print('father2.eating')
def smoking(self):
print('father2.somking')
class son(father1,father2):
def aaaa(self):
print('son.eating')
son = son()
son.eating()#result:father1,eating 从执行结果可以看出来当继承了两个类的时候在找方法的时候会优先先从左边的类开始查找,而且是一条道走到黑似的查找,
#当左边的父类总没有的时候会再从右边开始查找.
#假如当继承的两个父类都是继承自base类的时候会怎么查找呢,此时会先从左边一级一级开始向上查找一直找到base的子类,而后开始从右边查找
#而后逐步找到base类,总结就是说;当继承了两个父类,且父类都继承自base的时候,执行顺序就会,先左边然后一条道走到黑,发现没有然后执行右边的,最后查找
此时可以看下边的图,就会更加容易理解
多继承中还需要注意一点
class base():
def eating(self):
print('base.eating')
class mather():
def drinking(self):
print('mother.drinking')
class father(base):
def eating(self):
self.drinking()
def drinking(self):
print('father.drinking')
class son(mather,father):
def aaaa(self):
print('son.aaaa')
son = son()
son.eating()
执行结果:
看到结果的时候是不是很惊讶为什么执行出来和自己的预测不同,接下来通过图表的形式来看看这个方法是如何寻找的.
4.类之多态
其实在学数据类的时候就可以发现,在py中其实可以说是没有多态的.在其他的语言中例如java中的多态是这样的.
类
方法1(参数1,参数2,参数3){
方法体.....
}
方法1(参数1,参数2){
方法体.......
}
此时这种类型就是多态,同一个方法,因为参数的个数和类型不同就会构成多态,但是在py中参数没有强制类型也就是没有 String s之类的所以py中的多态就是原始的多态.
练习题小游戏.
创建三个游戏人物
孙悟空,男,3000,初试战斗力1000
猪八戒,男,4000,初试战斗力700
金角大王,男2000,初试战斗力,900
游戏场景
草丛战斗消耗200战斗力
自我修炼增长100战斗力
多人游戏消耗500战斗力
#youxi
class Person():
def __init__(self,name,age,gender,zhandouli):
self.name = name
self.age = age
self.gender = gender
self.zhandouli = zhandouli
def caocong(self):
if self.zhandouli > 200:
print(self.name,'开始草丛战斗',end="")
self.zhandouli -= 200
return self.zhandouli
else:
print('由于战斗力不足,您不能应用草丛战斗@!')
def xiulian(self):
print(self.name,'开始修炼,',end="")
self.zhandouli += 100
return self.zhandouli
def duorenyouxi(self):
if self.zhandouli > 500:
print(self.name,'开始进行多人游戏@!',end="")
self.zhandouli -= 500
return self.zhandouli
else:
print("由于战斗力不足,不能参加多人游戏!")
sunwukong = Person('孙悟空',3000,'男',1000)
zhubajie = Person('猪八戒',4000,'男',800)
jinjiaodawang = Person('金角大王',2000,'男',900)
#孙悟空参加草丛战斗
print('剩余战斗力是:',sunwukong.caocong())
print('剩余战斗力是:',zhubajie.xiulian())
print('剩余战斗力是:',jinjiaodawang.duorenyouxi())
面向对象之成员变量
什么是成员变量呢,类中的函数和字段都是成员变量,其中字段保存在对象中.方法保存在类中.
字段分为,普通字段,静态字段
普通字段:在def __init__(self,name):
self.name = name #这个self.name就是普通字段
同时普通字段只能通过对象访问 obj.
静态字段,定义在类里面,但是不在方法里面的字段是静态字段.
class foo():
name = 'sage'#这个name就是静态变量
def bar(self):
print('bar')
静态字段同过类访问,也可以通过对象访问
例如
class foo():
country = 'china'
def __init__(self,name):
self.name = name
#访问普通字段
obj = foo('sgin')
print(obj.name)
print(obj.country)
#访问静态字段
print(foo.country)
结果
我们从这里面可以看到,对象和类的关系就是,对象可以找打类,但是类找不到对象.
类中的方法
普通方法
静态方法
类方法
普通方法:保存在类里面,由对象来调用
静态方法:保存在类里面通过类来调用
类方法:跟静态方法几乎一样,只是参数的名字不同,由类来调用
class foo():
#普通方法
def putong(self):
print('普通')
@staticmethod#使用这个注解修饰这个方法是个静态方法
def staticmethod():#这里面的self不是必的须
print('staticmethod')
@classmethod#使用这个注解修饰这个方法是个类方法,与静态唯一不同的就是self要写成cls,cls就是当前的类名
def classmethod(cls):
print(cls,'classmethod')
obj = foo()
#调用普通方法
obj.putong()
foo.staticmethod()
foo.classmethod()
property属性
python中有一个不伦不类的成员就是,属性也叫做特性,这个既不是字段又不是函数
#property
class foo():
@property
def per(self):
print('123')
obj = foo()
obj.per#此时调用属性就像调用普通字段的方法相同不用再加括号 result: 123
属性类似于字段
属性的赋值
class foo():
@property
def per(self):
return 1
@per.setter #给属性per赋值需要使用此修饰符
def per(self,val):
self.val = val
print(self.val)
obj = foo()
print(obj.per)
obj.per = 123
属性就是跟字段的用法一样的不仅要可以访问还要可以赋值
属性的应用场景
对list进行一个简单的分页
li = []
for i in range(1000):
li.append(i)
while True:
inp = int(input('请输入页码'))
ret = li[(inp-1)*10:10*inp]
print(ret)
使用property进行操作
class page():
def __init__(self,currentpage):
try:
self.currentpage = int(currentpage)
except Exception as e:
self.currentpage = 1
@property
def start(self):
self.val = int(self.currentpage)
return (self.val-1)*10
@property
def end(self):
self.val = int(self.currentpage)
return self.val * 10
li = []
for i in range(1000):
li.append(i)
while True:
inp = input("请输入页码:")
obj = page(inp)#控制
print(li[obj.start:obj.end])
属性的另一种写法和形式:
class foo():
def f1(self):
return 123
per = property(fget=f1)
def f2(self,val):
self.val = val
print(self.val)
per = property(fset=f2)
def f3(self):
print('delf3')
per = property(fdel=f3)
#使用方式与原来相同
成员修饰符
在java中有public,private,protect三种修饰符,但是在py中只有两种修饰符,就是私有和公有
私有演示
class foo():
def __init__(self,name,age):
self.name = name
self.__age = age #此时_age 就是一个私有变量
obj = foo('sign',23)
print(obj.name)
print(obj.__age)
result:sign
Traceback (most recent call last):
File "C:/Users/zhangyakang/PycharmProjects/fullstack/class_object/class_object.py", line 251, in <module>
print(obj.__age)
AttributeError: 'foo' object has no attribute '__age'
私有字段的作用类中访问,不能直接访问只能间接访问如写一个get方法来访问
私有静态字段
class foo():
__v = '213'
print(foo.__v)
Traceback (most recent call last):
File "C:/Users/zhangyakang/PycharmProjects/fullstack/class_object/class_object.py", line 255, in <module>
print(foo.__v)
AttributeError: type object 'foo' has no attribute '__v'
方法的私有
在方法的前面加两个下划线就是私有变量
访问私有方法同样是不能直接访问,需要简介访问.
访问私有静态方法同样不能直接访问,需要间接访问
这里要知道,子类是没有办法访问父类的私有字段的.例如
class father():
__v = 'father'
class son(father):
v = 'son'
print(son.v)
print(son.__v)
Traceback (most recent call last):
son
File "C:/Users/zhangyakang/PycharmProjects/fullstack/class_object/class_object.py", line 263, in <module>
print(son.__v)
AttributeError: type object 'son' has no attribute '__v'
类中的特殊成员
__call__方法
class foo():
def __init__(self):
print('init')
def __call__(self, *args, **kwargs):
print('call')
obj = foo()
obj()
结果:
init
call类中的call方法是干什么用的呢,其实也没有发现有啥用,看源代码的时候看到有这样的写法就记录下来了.
对象+括号就会默认执行对象对应的类中的__call__方法
__str__方法:
class foo():
def __init__(self):
print("init")
def __str__(self):
print('str')
return 'str'
obj = foo()
print(str(obj))
init
str
str
在执行str(对象)的时候会自动执行对象的__str__()方法.__str__()方法必须有返回值
__init__()方法:
class foo():
def __init__(self):
print('init')
def __int__(self):
print('int')
return 23
obj = foo()
print(int(obj))
init
int
23
在执行int(对象)方法的时候,会自动执行对象中的__int__方法.同样也是必须有返回值.
__add__()方法:
class foo():
def __init__(self):
print('foo init')
def __add__(self, other):
self.other = other
print(self,type(self.other))
class bar():
def __init__(self):
print('bar init')
def __add__(self, other):
self.other = other
print(self,type(self.other))
obj_foo = foo()
obj_bar = bar()
obj_foo + obj_bar
结果:
foo init
bar init
<__main__.foo object at 0x000000000113CD30> <class '__main__.bar'>
ok:分析上面的结果发现两个对象相加会执行左边的对象里面的__add__()方法,然后将右边的对象作为了参数传入到了左边的对象的__add__()方法中
__del__():方法
class foo():
def __init__(self):
print('init')
def __del__(self):
print('del')
obj = foo()
del obj
init
del
del方法也叫析构方法会在销毁对象的时候执行__del__()方法.
__dict()__方法:
class foo():
def __init__(self,name,age,salary,job):
self.name = name
self.age = age
self.salary = salary
self.job = job
print('init')
def f1(self):
print('f1')
def f2(self):
print('f2')
def f3(self):
print('f3')
obj = foo('alex',23,2000,'it')
print(obj.__dict__)
print(foo.__dict__)
结果:
init
{'job': 'it', 'salary': 2000, 'name': 'alex', 'age': 23}
{'__dict__': <attribute '__dict__' of 'foo' objects>, 'f2': <function foo.f2 at 0x000000000112E378>, '__module__': '__main__', 'f3': <function foo.f3 at 0x000000000112E400>, 'f1': <function foo.f1 at 0x000000000112E2F0>, '__init__': <function foo.__init__ at 0x000000000112E268>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'foo' objects>}
对象执行dict方法的时候会将对象的字段和值放到字典中
类执行dict方法的时候会将类中的成员放到字典中.
__getitem__()方法:
class foo():
def __init__(self):
print('init')
def __getitem__(self, item):
print(item)
return item
obj = foo()
print(obj[8])
init
8
8
假若对象后面有[数字]会自动执行类中的__getitem__()方法,中括号中的参数就是item参数
__setitem__()方法:
class foo():
def __init__(self):
print('init')
def __setitem__(self, key, value):
print(key,value)
obj = foo()
obj[9] = 'value'
init
9 value
从执行结果看的出来, obj[key] = value 只要是这种形式的表达式就会执行对象的__setitem__()方法
__delitem__():方法
class foo():
def __delitem__(self, key):
print(key)
obj = foo()
del obj[9]
结果:
9
在执行 del 对象[数字] 语句的时候会自动执行对象中的 __delitem__()方法
对象切片
class foo():
def __getitem__(self, item):
print(type(item),item)
obj = foo()
print(obj[1:4:2])
<class 'slice'> slice(1, 4, 2)
None
从结果来看,item是slice类型. 并且slice类型是不可迭代的. 1:是item.start 4:是item.end 2:是item.step
__iter__():方法:
class foo():
def __iter__(self):
return [11,22,33,44]
obj = foo()
for i in obj.__iter__():
print(i)
结果:
11
22
33
44
执行__iter__()方法会返回一个迭代器,然后for循环会对迭代器进行迭代
class foo():
def func(self):
print(123)
obj = foo()
obj.func()
结果:
123
type类
test = type('test',(),{'age':12,'name':'alex'})
tes = test()
print(tes.age)
结果:12
带有方法的创建方法:
@staticmethod
def foo():
print('staticmethod')
@classmethod
def bar(cls):
print(cls)
def cho(self):
print(self)
test = type('test',(),{'foo':foo,'bar':bar,'cho':cho})
tes = test()
print(tes.cho())
print(test.bar())
print(test.foo())
结果:
<__main__.test object at 0x0000000000AB40B8>
None
<class '__main__.test'>
None
staticmethod
None
从上面可以看出来其实类也是type创建出来的对象.类都是type类的对象.
metaclass
class mytpye(type):
def __init__(self,*args,**kwargs):
pass
class foo(object,metaclass=mytpye):
def __init__(self):
pass
上面我们已经知道了,创建类的时候是默认继承type类假如更改不默认继承type类,则需要使用metaclass方法来更改.
在创建对象的时候会执行type类的call方法.
反射:
#反射
class foo():
def __init__(self,name,age):
self.name = name
self.age = age
def bar(self):
print('bar')
obj = foo('sign',19)
#使用反射获取成员
v = getattr(obj,'name')#result:sign
print(v)
#使用反射获取函数
s = getattr(obj,'bar')#result:<bound method foo.bar of <__main__.foo object at 0x00000000005BCF98>>
print(s)
#判断对象中是否有某个成员,有则是true否则就是false
print(hasattr(obj,'age'))#result:True
print(hasattr(obj,'lili'))#result:False
#利用反射更改字段的值
setattr(obj,'name','vvvvv')
print(getattr(obj,'name'))#result:vvvvv
#利用反射删除对象里的字段
delattr(obj,'name')
print(getattr(obj,'name'))#result:AttributeError: 'foo' object has no attribute 'name'
在python中是通过字符串的形式操作对象成员.
反射不仅可以找对象或者类中的成员,还可以找模块中的成员,方法与上面演示的代码相同.
单例模式
单例模式就是永远使用一个实例
class foo():
__v = None
@classmethod
def get_instance(cls):
if cls.__v :
return cls.__v
else:
cls.__v = foo()
return cls.__v
#注意点此时创建对象不再使用类名()的方式创建对象.而是调用单例模式的方法
obj1 = foo.get_instance()
print(obj1)
obj2 = foo.get_instance()
print(obj2)
#<__main__.foo object at 0x0000000000A3CD30>
#<__main__.foo object at 0x0000000000A3CD30>
#从执行结果来看调用get_instance()方法确确实实是创建了一个对象.