python面向对象

1,什么是对象,

  对象 即类   在python中将要描述的东西或者事物用类来表示 作为对象,

2对象的特点

  1封装:将要表示的对象具有的属性定义好,或者动作

  2继承:子类将自动拥有父类的非私有 类的属性

  3多态:即类的高度可扩展性

3

  1. 简述编写类和执行类中方法的流程
    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码的速度跑着  

 

    1. 将以下函数改成类的方式并调用:
       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同时支持单继承与多继承,当只有一个父类时为单继承,当存在多个父类时为多继承。

继承的性质特征:

子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的变量和方法。

调用方法执行顺序:(见案例)

在调用方法时,先调用子类自己的方法,子类无,再去父类中查找。查找顺序是自左(从上往下)向右。

如果多个父类,再无共同的父类,则自左执行到顶,再往右执行。

如果有共同的父类,则自左执行到倒数第二层,再向右执行,最后再执行最顶层。

 

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

 

案例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,优先执行自己的方法,再执行父类的方法

执行结果:

0?wx_fmt=png

案例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()

执行结果:

 

0?wx_fmt=png

案例3:多继承,无共同的父类,执行顺序:从左(先往上)再往右。

 

左右类无共同父类封顶的继承及执行的顺序:

0?wx_fmt=png


#先左,在左上,左上找到顶层,则再往右边的类找
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

执行结果:

0?wx_fmt=png

案例4:多继承,有共同的父类,执行到倒数第二层,再向右执行0?wx_fmt=png

#找到倒数第二层停止,再往后找,最后找共同

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

执行顺序:

0?wx_fmt=png

 

面向对象的第三个特征是多态:指参数的“有多种形式”、多种形态,意味着就算不知道变量所引用的对象类是什么,还是能对它进行操作,而它也会根据对象(或类)类型的不同而表现出不同的行为。

   其他编程语言如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
View Code

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()

 

posted @ 2018-09-25 15:23  Big_C  阅读(180)  评论(0编辑  收藏  举报