python面向对象
1,什么是对象,
对象 即类 在python中将要描述的东西或者事物用类来表示 作为对象,
2对象的特点
1封装:将要表示的对象具有的属性定义好,或者动作
2继承:子类将自动拥有父类的非私有 类的属性
3多态:即类的高度可扩展性
3
- 简述编写类和执行类中方法的流程
class Car: def __init__(self,color,speed): self.color=color self.speed=speed def msg(self): print("%s的车以%s的速度跑着"%(self.color,self.speed)) my_car=Car("金色","80码") my_car.msg()
》》》金色的车以80码的速度跑着
- 将以下函数改成类的方式并调用:
def func(a1):
print(a1)
class func: def func(self,a1): print(a1)
方法和函数的区别?
首先摒弃错误认知:并不是类中的调用都叫方法
看举例看代码
class Foo(object): def func(self): pass #实例化 obj = Foo() # 执行方式一:调用的func是方法 obj.func() #func 方法 # 执行方式二:调用的func是函数 Foo.func(123) # 函数
是的!例子中很明确,类对象调用func是方法,类调用func是函数,并且是自己传递参数123!
最大的区别是参数的传递参数,方法是自动传参self,函数是主动传参
那么以后我们就可以直接看参数是如何传递的来判断,
如果还不确定可以打印类型看看
from types import FunctionType,MethodType print(isinstance(obj.func,MethodType)) ---># True print(isinstance(Foo.func,FunctionType)) ---># True
什么是构造方法?
构造方法与其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法。
在python中创建一个构造方法很简单,只需要把init方法的名字从简单的init修改为魔法版本__init__即可。
class foobar:
def __init__(self):
self.somevar = 42
>>> f = foobar()
>>> f.somevar
42
如果给构造方法传递几个参数的话,会怎么样呢?
>>> class foobar:
... def __init__(self, value = 42):
... self.somevar = value
...
>>> f = foobar('this is a test')
>>> f.somevar
'this is a test'
**************************************************************************
重写一般方法和特殊的构造方法
每个类都可能有一个或者多个超类,它们从超类那里继承行为方式,如果一个方法在b类的一个实例中被调用,但在b类中没有找到该方法,那么就去它的超类a里面找
class a:
def hello(self):
print "hello,i'am a."
class b(a)
a类定义了一个叫做hello的方法,被b类继承,下面是一个说明类是如何工作的列子
>>>A = a()
>>>B = b()
>>>A.hello()hello,i'am a
>>>B.hello()
hello,i'am a
因为b类没有定义自己的hello方法,所以当hello被调用的时候,原始的信息就被打印出来。
在子类中增加功能的最基本的方法就是增加方法,但是也可以重写一些超类的方法来自定义继承的行为。b类也能重写这个方法,比如下面的列子中b类的定义被修改了。
class b(a):
def hello(self):
print "hello,i'am b."
重写是继承机制中的一个重要内容,对于构造方法尤其重要,构造方法用来初始化新创建的对象的状态,大多数子类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码,虽然重写的机制对于所有方法来说都是一样的,但是当重写构造方法和普通重写方法更容易遇到特别的问题,比如,如果一个类的构造方法被重写,那么就需要调用超类的构造方法,否则,对象可能不会被正确的初始化。
面向对象中的self指的是什么?
刚开始学习Python的类写法的时候觉得很是麻烦,为什么定义时需要而调用时又不需要,为什么不能内部简化从而减少我们敲击键盘的次数? 你看完这篇文章后就会明白所有的疑问。 self代表类的实例,而非类。 实例来说明
class Test: def prt(self): print(self) print(self.__class__) t = Test() t.prt()
执行结果如下
<__main__.Test object at 0x000000000284E080> <class '__main__.Test'>
从上面的例子中可以很明显的看出,self代表的是类的实例。而self.__class__则指向类。 self不必非写成self 有很多童鞋是先学习别的语言然后学习Python的,所以总觉得self怪怪的,想写成this,可以吗? 当然可以,还是把上面的代码改写一下。
class Test: def prt(this): print(this) print(this.__class__) t = Test() t.prt()
改成this后,运行结果完全一样。 当然,最好还是尊重约定俗成的习惯,使用self。 self可以不写吗 在Python的解释器内部,当我们调用t.prt()时,实际上Python解释成Test.prt(t),也就是说把self替换成类的实例。 有兴趣的童鞋可以把上面的t.prt()一行改写一下,运行后的实际结果完全相同。 实际上已经部分说明了self在定义时不可以省略,如果非要试一下,那么请看下面:
class Test: def prt(): print(self) t = Test() t.prt()
运行时提醒错误如下:prt在定义时没有参数,但是我们运行时强行传了一个参数。 由于上面解释过了t.prt()等同于Test.prt(t),所以程序提醒我们多传了一个参数t。
Traceback (most recent call last): File "h.py", line 6, in <module> t.prt() TypeError: prt() takes 0 positional arguments but 1 was given
当然,如果我们的定义和调用时均不传类实例是可以的,这就是类方法。
class Test: def prt(): print(__class__) Test.prt()
运行结果如下 <class '__main__.test'="">
在继承时,传入的是哪个实例,就是那个传入的实例,而不是指定义了self的类的实例。
class Parent: def pprt(self): print(self) class Child(Parent): def cprt(self): print(self) c = Child() c.cprt() c.pprt() p = Parent() p.pprt()
运行结果如下
<__main__.Child object at 0x0000000002A47080> <__main__.Child object at 0x0000000002A47080> <__main__.Parent object at 0x0000000002A47240>
解释: 运行c.cprt()时应该没有理解问题,指的是Child类的实例。 但是在运行c.pprt()时,等同于Child.pprt(c),所以self指的依然是Child类的实例,由于self中没有定义pprt()方法,所以沿着继承树往上找,发现在父类Parent中定义了pprt()方法,所以就会成功调用。 在描述符类中,self指的是描述符类的实例 不太容易理解,先看实例:
class Desc: def __get__(self, ins, cls): print('self in Desc: %s ' % self ) print(self, ins, cls) class Test: x = Desc() def prt(self): print('self in Test: %s' % self) t = Test() t.prt() t.x
self in Test: <__main__.Test object at 0x0000000002A570B8> self in Desc: <__main__.Desc object at 0x000000000283E208> <__main__.Desc object at 0x000000000283E208> <__main__.Test object at 0x0000000002A570B8> <class '__main__.Test'>
题外话:由于在很多时候描述符类中仍然需要知道调用该描述符的实例是谁,所以在描述符类中存在第二个参数ins,用来表示调用它的类实例,所以t.x时可以看到第三行中的运行结果中第二项为。而采用Test.x进行调用时,由于没有实例,所以返回None。 总结 self在定义时需要定义,但是在调用时会自动传入。 self的名字并不是规定死的,但是最好还是按照约定是用self self总是指调用时的类的实例。 以上所有代码在Python3.4中均测试通过。
class cirle: def __init__(self,PI): self.PI=PI def s(self,r): return self.PI*r**2 def long(self,r): return 2*self.PI*r cirle=cirle(3.14) print(cirle.s(3)) print(cirle.long(3))
面向对象中为什么要有继承?
继承
我们不想把同一段代码写好几,之前使用的函数避免了这种情况。但现在又有个更微妙的问题。如果已经有了一个类,又想建立一个非常类似的类,只是添加几个方法。
比如有动物类,我们又想在动物类的基础上建立鸟类、鱼类,哺乳动物类。
面向对象的第二个特征是继承。
可以将多个类共有的方法提取到父类中,子类仅需继承父类;
基本语法为class新类名(父类1,父类2,..)
单继承与多继承区别:
Python同时支持单继承与多继承,当只有一个父类时为单继承,当存在多个父类时为多继承。
继承的性质特征:
子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的变量和方法。
调用方法执行顺序:(见案例)
在调用方法时,先调用子类自己的方法,子类无,再去父类中查找。查找顺序是自左(从上往下)向右。
如果多个父类,再无共同的父类,则自左执行到顶,再往右执行。
如果有共同的父类,则自左执行到倒数第二层,再向右执行,最后再执行最顶层。
案例1:
F2继承F1:类似于把F1中的功能在F2中又写了一遍,如果有一个方法F2自己无,则去父类F1中去找找看
class F1:#父类,基类
def show(self):
print('show')
def foo(self):
print(self.name)
class F2(F1):#把F1的方法再次引用到F2;子类、派生类
def __init__(self,name):
self.name=name
def bar(self):
print('bar')
def show(self):
print('F2.show')
obj=F2('hh')
print('-----foo方法执行结果--------')
obj.foo()#执行结果:hh,需要去父类里面找
print('-----show方法执行结果--------')
obj.show()#执行结果F2.show,优先执行自己的方法,再执行父类的方法
执行结果:
案例2:如果父类和子类有相同的方法,则优先执行子类自己的方法,如果子类无再去父类找
#self一直代表的是对象自己
# 如果一个方法,自己有,则执行自己的方法,如果自己没有,再去往上去父类寻找
class S1:
def F1(self):
self.F2()
def F2(self):
print('S1.F2')
class S2(S1):
def F3(self):
self.F1()
def F2(self):
print('S2.F2')
print('----S1.F2-------------')
#实例化s1
obj1 = S1()
obj1.F1() # 先去S1执行F1,再看S1中有没有F2,有,则执行自己的F2
print('----S2.F3-------------')
#实例化s2
obj2=S2()
#1)先去s2执行F3,因为s2自己无F1,去S1中执行F1,F1方法需要执行F2
#2)self代表s2本身,因s2自己有F2,再执行S2的F2
obj2.F3()
执行结果:
案例3:多继承,无共同的父类,执行顺序:从左(先往上)再往右。
左右类无共同父类封顶的继承及执行的顺序:
#先左,在左上,左上找到顶层,则再往右边的类找
class c0:
def f2(self):
print('c0.f2')
class c1(c0):
def f1(self):
print('c1.f1')
class c2:
def f2(self):
print('c2.f2')
def f4(self):
print('c2.f4')
class c3(c1,c2):
def f3(self):
pass
obj=c3()#实例化c3对象
print('------c3.f2-------')
obj.f2()#先去找c3,c3无f2,再去c1,c1无f2,则去c0找
print('------c3.f1-------')
obj.f1()#先去找c3,c3无f1,再去c1
print('------c3.f4-------')
obj.f4()#先去找c3,c3无f4,再去c1,c1无f4,则去c0找,c0无f4,再去c2
执行结果:
案例4:多继承,有共同的父类,执行到倒数第二层,再向右执行
#找到倒数第二层停止,再往后找,最后找共同
class c0:
def f4(self):
print('c0.f2')
class c_2(c0):
def f2(self):
print('c_2.f2')
class c21(c_2):
def f1(self):
print('c21.f1')
class c_1(c0):
def f1(self):
print('c_1.f2')
class c11(c_1):
def f1(self):
print('c11.f1')
class c3(c11,c21):
def f3(self):
print('c3.f3')
#实例化对象
obj=c3()
obj.f3()#依次找寻c3找到,执行结果c3.f3
obj.f1()#依次找寻c3,c11找到,执行结果c11.f1
obj.f2()#依次找寻c3,c11,c_1,c21,c_2,找到,执行结果c_2.f2
obj.f4()#依次找寻c3,c11,c_1,c21,c_2,c0找到,执行结果,c0.f2
执行顺序:
面向对象的第三个特征是多态:指参数的“有多种形式”、多种形态,意味着就算不知道变量所引用的对象类是什么,还是能对它进行操作,而它也会根据对象(或类)类型的不同而表现出不同的行为。
其他编程语言如java在定义参数时,需要制定参数类型,且传入参数时,需要符合类型要求。但python基本上不用管这个特征,因为python定义变量时,可以不指定类型。如下案例,可以传入不同的参数类型,比如字典、列表、数字、string等。
比如案例:
def func(arg):
print(arg)
func(1)
func("hh")
func(["a","b",6])
func({"a":1,"c":2})
案例链接:https://pan.baidu.com/s/1skU53lV 密码:ifah
# #案例1:F2继承F1,类似于把F1中的功能在F2中又写了一遍,如果有一个方法F2自己无,则去父类F1中去找找看 # class F1:#父类,基类 # def show(self): # print('show') # def foo(self): # print(self.name) # class F2(F1):#把F1的方法再次引用到F2;子类、派生类 # def __init__(self,name): # self.name=name # def bar(self): # print('bar') # def show(self): # print('F2.show') # obj=F2('hh') # print('-----foo方法执行结果--------') # obj.foo()#执行结果:hh,需要去父类里面找 # print('-----show方法执行结果--------') # obj.show()#执行结果F2.show,优先执行自己的方法,再执行父类的方法 # #案例2:如果父类和子类有相同的方法,则优先执行子类自己的方法,如果子类无再去父类找 # #self一直代表的是对象自己 # # 如果一个方法,自己有,则执行自己的方法,如果自己没有,再去往上去父类寻找 # class S1: # def F1(self): # self.F2() # def F2(self): # print('S1.F2') # class S2(S1): # def F3(self): # self.F1() # def F2(self): # print('S2.F2') # # print('----S1.F2-------------') # #实例化s1 # obj1 = S1() # obj1.F1() # 先去S1执行F1,再看S1中有没有F2,有,则执行自己的F2 # print('----S2.F3-------------') # #实例化s2 # obj2=S2() # #1)先去s2执行F3,因为s2自己无F1,去S1中执行F1,F1方法需要执行F2 # #2)self代表s2本身,因s2自己有F2,再执行S2的F2 # obj2.F3() # #案例3:多继承,从左(先往上)再往右 # #先左,在左上,左上找到顶层,则再往右边的类找 # class c0: # def f2(self): # print('c0.f2') # class c1(c0): # def f1(self): # print('c1.f1') # class c2: # def f2(self): # print('c2.f2') # def f4(self): # print('c2.f4') # class c3(c1,c2): # def f3(self): # pass # obj=c3()#实例化c3对象 # print('------c3.f2-------') # obj.f2()#先去找c3,c3无f2,再去c1,c1无f2,则去c0找 # print('------c3.f1-------') # obj.f1()#先去找c3,c3无f1,再去c1 # print('------c3.f4-------') # obj.f4()#先去找c3,c3无f4,再去c1,c1无f4,则去c0找,c0无f4,再去c2 class c0: def f4(self): print('c0.f2') class c_2(c0): def f2(self): print('c_2.f2') class c21(c_2): def f1(self): print('c21.f1') class c_1(c0): def f1(self): print('c_1.f2') class c11(c_1): def f1(self): print('c11.f1') class c3(c11,c21): def f3(self): print('c3.f3') obj=c3() obj.f3()#依次找寻c3找到,执行结果c3.f3 obj.f1()#依次找寻c3,c11找到,执行结果c11.f1 obj.f2()#依次找寻c3,c11,c_1,c21,c_2,找到,执行结果c_2.f2 obj.f4()#依次找寻c3,c11,c_1,c21,c_2,c0找到,执行结果,c0.f2
Python多继承时,查找成员的顺序遵循什么规则?
python中使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承,也叫菱形继承问题)等
MRO
MRO即method resolution order,用于判断子类调用的属性来自于哪个父类。在Python2.3之前,MRO是基于深度优先算法的,自2.3开始使用C3算法,定义类时需要继承object,这样的类称为新式类,否则为旧式类
从图中可以看出,旧式类查找属性时是深度优先搜索,新式类则是广度优先搜索
C3算法最早被提出是用于Lisp的,应用在Python中是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性的问题。
- 本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。
- 单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序
示例
看下面的例子
class X(object): def f(self): print 'x' class A(X): def f(self): print 'a' def extral(self): print 'extral a' class B(X): def f(self): print 'b' def extral(self): print 'extral b' class C(A, B, X): def f(self): super(C, self).f() print 'c' print C.mro() c = C() c.f() c.extral()
根据广度搜索原则最先搜索到A,所以结果很明显,如下所示
类C没有extral函数,调用的是子类的该函数。这种类的部分行为由父类来提供的行为,叫做抽象超类.
补充代码实现:
user_list = []
while True:
user = input(“请输入用户名:”)
pwd = input(“请输入密码:”)
email = input(“请输入邮箱:”)
user_list = [] class Account: def __init__(self,user,pwd,email): self.user=user self.pwd=pwd self.email=email while True: user = input("请输入用户名:") pwd = input("请输入密码:") email = input("请输入邮箱:") Account = Account(user, pwd, email) user_list.append(Account) if len(user_list)==3: for el in user_list: print("我叫%s,邮箱是%s"%(el.user,el.email)) break
补充代码
class User: def __init__(self,name,pwd): self.name=name self.pwd=pwd class Account: def __init__(self): self.user_list=[] def login(self,input_user,input_pwd): self.input_user=input_user self.input_pwd=input_pwd if input_user=="bigc" and input_pwd=="123": print("恭喜登录成功!") return True else: return False def resiger(self,user,pwd): # self.user=input("输入用户名:") # self.pwd=input("输入密码:") self.user_list.append(user) self.user_list.append(pwd) f=open("User.txt",mode="a",encoding="utf-8") f.write(str(self.user_list)+"\n") f.flush() f.close() return def run(self): Account.resiger(user=input("输入注册用户名:"),pwd=input("输入密码:")) Account.resiger(user=input("输入注册用户名:"),pwd=input("输入密码:")) count=0 while count<3: if Account.login(input_user=input("输入你要登录的用户名:"),input_pwd=input("输入登录密码:")): break else: print("用户名或密码错误,你还剩%s次机会登录!"%(2-count)) count+=1 return Account=Account() Account.run()
class User: def __init__(self,name,pwd): self.name=name self.pwd=pwd class Account: def __init__(self): self.user_list=[] def login(self,input_user,input_pwd): for el in self.user_list: if input_user==el.name and input_pwd==el.pwd: print("恭喜登录成功!") return True else: return False def resiger(self): user=input("输入用户名:") pwd=input("输入密码:") user self.user_list.append(User(user,pwd)) for el in self.user_list: f=open("User.txt",mode="a",encoding="utf-8") f.write(str(el.name)+"_"+str(el.pwd)+"\n") f.flush() f.close() return def run(self): Account.resiger() Account.resiger() count=0 while count<3: if Account.login(input_user=input("输入你要登录的用户名:"),input_pwd=input("输入登录密码:")): break else: print("用户名或密码错误,你还剩%s次机会登录!"%(2-count)) count+=1 return Account=Account() Account.run()
class User: def __init__(self,name,pwd): self.name=name self.pwd=pwd class Account: def __init__(self): self.user_list=[] def login(self): user = input("输入你要登录的用户名:") pwd = input("输入登录密码:") f = open("User.txt", mode="r", encoding="UTF-8") for line in f: if line.strip() == user+"_"+pwd: f.close() return True else: f.close() return False def chack_save(self,user, pwd): # wusir # 1. 检查用户名是否重复 f = open("User.txt", mode="r+", encoding="utf-8") for line in f: if line == "": # 防止空行影响程序运行 continue user_info_username = line.split("_")[0] if user == user_info_username: # 用户名重复了 return False else: # 2. 写入到文件中 f.write(user+"_"+pwd+"\n") f.flush() f.close() return True def resiger(self): user=input("输入用户名:") pwd=input("输入密码:") self.chack_save(user,pwd) def run(self): self.resiger() self.resiger() count=0 while count<3: if self.login(): break else: print("用户名或密码错误,你还剩%s次机会登录!"%(2-count)) count+=1 return accunt=Account() accunt.run()