• python类、super函数


    #PYTHON语言及其应用学习笔记

    1、创建简单的类

    class Person():
        #python中特殊的对象初始化方法__init__,一个特殊的函数名
        #当你在类声明里定义__init__()方法时,第一个参数必须为self。
        def __init__(self,name):
            self.name=name
    someone=Person('elmer fudd')
    #上面一行代码实际做了如下工作:
    1、查看Person类的定义
    2、在内存中实例化(创建)一个新的对象
    3、调用对象的__init__方法,将这个新创建的对象作为self传入,并将另一个参数
    (‘elmer fudd’)作为name传入;
    4、将name的值存入对象;
    5、返回这个新的对象;
    6、将名字hunter与这个对象关联。

    2、类对象当做参数传递给函数,当做函数返回结果

    #这个新对象与其他的python对象一样。你可以把它当做列表、元组、字典或集合中的元素,也可以把它当做参数传递给函数,或者把它作为函数的返回结果
    class Person:
        def __init__(self,name):
            self.name=name
        def run(self):
            print(self.name + 'is running!')
    hunter=Person('elmer fudd')
    def hi(x):
        print(x.name)
        print(x.run())
    hi(hunter)  #elmer fudd  elmer fuddis running!  None
    #在Person类定义的内部,你可以直接通过self.name访问name属性,而当你创建了一个实际的对象后,例如这里的hunter,需要通过hunter.name来访问它
    #在类的定义中,__init__并不是必需的。只有当需要区分由该类创建的不同对象时,才需要指定__init__方法。

    3、继承

           在你编写代码解决实际问题时,经常能找到一些已有的类,它们能够实现你所需的大部分
    功能,但不是全部。这时该怎么办?当然,你可以对这个已有的类进行修改,但这么做很
    容易让代码变得更加复杂,一不留神就可能会破坏原来可以正常工作的功能。
           当然,也可以另起炉灶重新编写一个类:复制粘贴原来的代码再融入自己的新代码。但这
    意味着你需要维护更多的代码。 同时,新类和旧类中实现同样功能的代码被分隔在了不同
    的地方(日后修改时需要改动多处)。
            更好的解决方法是利用类的继承:从已有类中衍生出新的类, 添加或修改部分功能。这是
    代码复用的一个绝佳的例子。 使用继承得到的新类会自动获得旧类中的所有方法,而不需
    要进行任何复制。
            你只需要在新类里面定义自己额外需要的方法, 或者按照需求对继承的方法进行修改即
    可。修改得到的新方法会覆盖原有的方法。 我们习惯将原始的类称为父类、 超类或基类,
    将新的类称作孩子类、 子类或衍生类。这些术语在面向对象的编程中不加以区分。

    class Car:
        def exclaim(self):
            print('i am a car!')
    class Yugo(Car):
        pass
    #为每一个类各创建一个对象,并调用刚刚声明的 exclaim 方法
    give_me_a_car=Car()
    give_me_a_yugo=Yugo()
    give_me_a_car.exclaim()    #i am a car!
    give_me_a_yugo.exclaim()   #i am a car!
    #我们不需要进行任何特殊的操作,Yugo就自动从Car那里继承了exclaim()方法。但事
    #实上,我们并不希望Yugo在exlaim()方法里宣称它是一个Car,这可能会造成身份危机
    #(无法区分Car和Yugo)。让我们来看看怎么解决这个问题。

    4、覆盖方法

    在子类中,可以覆盖任何父类的方法,包括__init__()

    class Car:
        def exclaim(self):
            print('i am a car!')
    class Yugo(Car):
        def exclaim(self):
            print('i am a yugo! much like a car, but more yugo-ish')
    #为每一个类各创建一个对象,并调用刚刚声明的 exclaim 方法
    give_me_a_car=Car()
    give_me_a_yugo=Yugo()
    give_me_a_car.exclaim()  #i am a car! 
    give_me_a_yugo.exclaim() #i am a yugo! much like a car, but more yugo-ish
    
    #在子类中,可以覆盖任何父类的方法,包括 __init__()。
    #医生( MDPerson)和律师( JDPerson)
    class Person():
        def __init__(self,name):
            self.name=name
        
    class MDPerson(Person):
        def __init__(self,name):
            self.name=',Doctor '+name
            
    class JDPerson(Person):
        def __init__(self,name):
            self.name=', '+name+' esquire'        
    person=Person('fudd')
    doctor=MDPerson('fund')
    lawyer=JDPerson('fund')
    print(person.name,doctor.name,lawyer.name) #fudd ,Doctor fund , fund esquire

    5、添加新方法

    子类还可以添加父类中没有的方法。 

    class Car():
        def exclaim(self):
            print("I'm a Car!")
    
    class Yugo(Car):
        def exclaim(self):
            print("I'm a Yugo! Much like a Car, but more Yugo-ish.")
        def need_a_push(self):
            print("A little help here?")

    6、使用super从父类得到帮助

    我们知道如何在子类中覆盖父类的方法,但如果想要调用父类的方法怎么办?  ##继承可以直接调用父类的方法,这里方法是初始化方法(__init__)。

    “哈哈!终于等到你问这个了。” super() 站出来说道。下面的例子将定义一个新的类
    EmailPerson,用于表示有电子邮箱的 Person
     

    class Person():
        def __init__(self,name,age):
            self.name=name
            self.age=age
    #下面是子类的定义,注意,子类的初始化方法__init__()中添加了一个额外的email参数:
    class EmailPerson(Person):
        def __init__(self,name,age,email):
            super().__init__(name,age)
            self.email=email
    email=EmailPerson('yangqie',18,'qq.com')
    print(email.name,email.email,email.age)  #yangqie qq.com 18
    在子类中定义__init__()方法时,父类的__init__()方法会被覆盖。因此,在子类中,
    父类的初始化方法并不会被自动调用,我们必须显示地调用它。
    只能全部调用或者全部不调用,上面例子要不就全部调用name、age,要不就都不调用。
    以上代码实际上做了这样几件事情:
    1、通过super()方法获取了父类Person的定义
    2、子类的__init__()调用了Person.__init__()方法。它会自动将self参数传递给父类。
       因此,你只需传入其余参数即可(除去self后的参数)。上面例子即name、age
    3、self.email=email这行新的代码才真正起到了将EmailPerson与Person区别开的作用。
    #为什么不像下面这样定义EmailPerson类呢?
    class EmailPerson(Person):
        def __init__(self,name,age,email):
            self.name=name
            self.age=age
            self.email=email
    确实可以这样做,但这有悖我们使用继承的初衷。我们应该使用super()来让Person完成它应该做的事情,
    就像任何一个单纯的Person对象一样。此外,如果Person类的定义在未来发生改变,使用super()可以
    保证这些改变会自动反映到EmailPerson类上,而不需手动修改。
    子类可以按照自己的方式处理问题,但如果仍需要借助父类的帮助,使用super()是最佳选择。

    super写法注意:

    1、子类的初始化方法__init__()中需要添加父类中init实例特性property,如上例中:

    def __init__(self,name,age,email)    name age 均为父类中init实例特性

    2、Python3与Python2写法不同

    Python2:super(Person,self).__init__(name,age)

    Python3:super().__init__(name,age)

    3、有如下几个问题:子类继承父类,创建子类实例时,要输入父类中的实例特性参数。

    问题1:如果子类没有init方法,父类有init方法,构建实例时要输入父类的实例特性参数吗?需要,子类继承了父类的init方法,需要输入!!!

                 如下面代码1,修改为child=Child('yq')即可。子类没有init方法时,继承父类init方法。

    问题2:如果子类有和父类相同的实例特性参数,构建实例时需要输入两个实例特性参数吗?如下面代码2,不用输入两个,输入一个即可。

         子类覆盖了父类的init方法,所以不需要输入!!

    问题3:如果子类中有init方法,父类中有init方法,构建实例时需要输入子类和父类中的实例特性参数吗?如下面代码3,只用输入子类实例特性参数。

    总结:在没有使用super情况下,子类有init,就用子类的,子类没有找父类,父类有,用父类的,父类没有,就不用管了。

    问题4:在使用super情况下,子类和父类中有相同实例特性参数,输入多少个呢?相同的输入一个即可,如代码4

    代码1
    class
    Model: def __init__(self,name): self.name=name class Child(Model): def save(self): print('ok') child=Child() #TypeError: __init__() missing 1 required positional argument: 'name'
    代码2
    class
    Model: def __init__(self,name): self.name=name class Child(Model): def __init__(self,name): self.name=name def save(self): print('ok') child=Child('yq') #ok
    代码3
    class Model:
        def __init__(self,age):
            self.age=age
    class Child(Model):
        def __init__(self,name):
            self.name=name
        def save(self):
            print('ok')
    child=Child('yq',20)
    child.save()  #__init__() takes 2 positional arguments but 3 were given
    代码4
    class Model:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    class Child(Model):
        def __init__(self,name,age):
            super().__init__(name,age)
            self.name=name
        def save(self):
            print('ok')
    child=Child('yq',20)
    child.save()  #ok

    下面再给出一个例子,用super调用父类方法,上面例子为__init__()方法,下面为super方法来调用父类中非__init__()方法

    不过我们应该不会这样写,直接用子类覆盖掉父类方法即可,这样写感觉比较混乱。

    class A:
        def read(self,name2):
            self.name2=name2
            print('{} is read'.format(self.name2))
    class B(A):
        def read(self,name1,name2):
            self.name1=name1
            print(self.name1)
            super().read(name2)
            print('{} and {} read:python is very good!'.format(self.name1,self.name2))
    a=B()
    a.read('cmj','yq')
    # cmj
    # yq is read
    # cmj and yq read:python is very good!

     7、self的自辩

    python中经常被争议一点就是必须把self设置为实例方法的第一个参数。python使用self参数来找到正确的对象所包含的特性和方法。

    car=Car()
    car.exclaim()   #i am a Car!

    python在背后做了以下两件事情:

    1、查找car对象所属的类(Car);

    2、把car对象作为self参数传给Car类所包含的exclaim()方法

    8、使用属性对特性进行访问和设置 装饰器

    # 修饰符( decorator)。下一个例子会定义两个不同的方法,它
    # 们都叫 name(),但包含不同的修饰符:
    # • @property,用于指示 getter 方法;
    # • @name.setter,用于指示 setter 方法。
    class Duck:
        def __init__(self,input_name):
            self.hidden_name=input_name
        @property
        def name(self):
            print('inside the getter')
            return self.hidden_name
        #如果你没有指定某一特性的 setter 属性( @diameter.setter),
        #那么将无法从类的外部对它的值进行设置。
        @name.setter
        def name(self,input_name):
            print('inside the setter')
            self.hidden_name=input_name
    fowl=Duck('yang')
    fowl.name='abc'   #输出 inside the setter 调用了name.setter属性
    print(fowl.name)  #输出inside the getter   abc

    9、使用名称重整保护私有属性

    怎么写怎么调用即可

    10、方法的类型

    有些属性和方法是类本身的一部分,还有一些是由类创建的实例的一部分

    在类的定义中,以self作为第一个参数的方法都是实例方法(instance method)。他们在创建自定义类时最常用。实例方法的首个参数是self,

    当它被调用时,python会把调用该方法的对象作为self参数传入。

    与之相对,类方法(classmethod)会作用于整个类,对类作出的任何改变会对它的所有实例对象产生影响。在类定义内部,用前缀修饰符

    @classmethod指定的方法都是类方法。与实例方法类似,类方法的第一个参数都是类本身。在Python中,这个参数常被写作cls,因为全称

    class是保留字,在这里无法使用。

    类方法不能访问实例属性!!参数必须传入cls!!(即代表了此类对象与self代表实例对象区别),并且用此来调用类属性:cls.类属性名

    静态方法不能访问实例属性!!参数不能传入self!!与类相关当事不依赖类与实例的方法!!

    ##静态方法与类方法都可以通过类火灾实例来调用,其两个特点都是不能调用实例属性。

    类定义三种类型:类属性、类方法和静态方法
    class A():
        count=0
        def __init__(self):
            A.count+=1
        def exclaim(self):
            print("i'm an A!")
        @classmethod
        def kids(cls):
            print('A has',cls.count,'little objects')  #cls.count 等价于 A.count
            #print('A has',A.count,'little objects')
        @staticmethod
        def hi():
            print('hi,jim!')
    easy_a=A()
    print(A.kids())  #A has 1 little objects
    breezy_a=A()
    print(A.kids())  #A has 2 little objects
    wheezy_a=A()
    A.kids()         #A has 3 little objects
    A.hi()           #hi,jim!
    注意,上面的代码中,我们使用的是A.count(类属性,有别于实例属性self.count),在kids()
    方法中,我们使用的是cls.count,它与A.count的作用一样
    类定义中的静态方法(static method),用@staticmethod修饰,既不会影响类的对象,
    出现只是为了方便

     思考如下代码

    class A:
        a=10
        def run():
            print('a is running')
    a=A()
    a.run()
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-60-c7e444286c71> in <module>()
          4         print('a is running')
          5 a=A()
    ----> 6 a.run()
    
    TypeError: run() takes 0 positional arguments but 1 was given
    class A:
        a=10
        def run(self):
            print('a is running')
    a=A()
    a.run() #a is running
    class A:
        a=10
        @classmethod
        def run(cls):  #必须要cls参数,否则报TypeError: run() takes 0 positional arguments but 1 was given
        print('a is running') 
    a=A() 
    a.run() #a is running

    使用类方法来调用类属性

    class A:
        a='jim'
        @classmethod
        def run(cls):
            b=a
            print('{} is running'.format(b))
    x=A()
    print(x.run())  #<__main__.A object at 0x00000188456C6470> is running
    class A:
        a='jim'
        @classmethod
        def run(cls):
            b=cls.a
            print('{} is running'.format(b))
    x=A()
    print(x.run())  #jim is running

    11、鸭子类型

    当我们输入像 a = 3 + 8 这样的式子时,整数 3 和 8 是怎么知道如何实现 + 的?同样, a又是怎么知道如何使用 = 来获取计算结果的?你可以使用 Python 的特殊方法( special method),有时也被称作魔术方法( magic method),来实现这些操作符的功能。
    这些特殊方法的名称以双下划线( __)开头和结束。没错,你已经见过其中一个: __init__,它根据类的定义以及传入的参数对新创建的对象进行初始化。假设你有一个简单的 Word 类,现在想要添加一个 equals() 方法来比较两个词是否一致,忽略大小写。也就是说,一个包含值 'ha' 的 Word 对象与包含 'HA' 的是相同的。

    class Word():
        def __init__(self,text):
            self.text=text
        def equals(self,word2):
            return self.text.lower()==word2.text.lower()
    first=Word('ha')
    second=Word('HA')
    third=Word('eh')
    print(first==second)         #False
    print(first.equals(second))  #True
    print(first.equals(third))   #False
    class Word():
        def __init__(self,text):
            self.text=text
        def __eq__(self,word2):
            return self.text.lower()==word2.text.lower()
        def __add__(self,word3):
            return self.text+word3.text
        def __mul__(self,word4):
            return self.text*word4.text
    first=Word('ha')
    second=Word('HA')
    third=Word('eh')
    four=Word('yq')
    five=Word(4)
    if __name__=='__main__':
        print(first==second)         #True
        print(first==third)          #False
        print(four+third)            #yqeh
        print(first*five)            #hahahaha
    # 太神奇了!是不是如同魔术一般?仅需将方法名改为 Python 里进行相等比较的特殊方法名
    # __eq__() 即可。

     12、组合

    如果你想要创建的子类在大多数情况下的行为都和父类相似的话(子类是父类的一种特
    殊情况,它们之间是 is-a 的关系),使用继承是非常不错的选择。建立复杂的继承关系确
    实很吸引人, 但有些时候使用组合composition) 或聚合aggregation) 更加符合现实的
    逻辑( x 含有 y,它们之间是 has-a 的关系)。

    class Tail():
        def __init__(self,length):
            self.length=length
    class Duck():
        def __init__(self,bill,tail):
            self.bill=bill
            self.tail=tail
        def about(self):
            print('this duck has a',bill.description,'bill and a',
                 tail.length,'tail')
    tail=Tail('long')
    bill=Bill('wide orange')
    duck=Duck(bill,tail)
    duck.about()  #this duck has a wide orange bill and a long tail

    13、何时使用类和对象而不是模块

    当你需要许多具有相似行为( 方法)但不同状态(特性)的实例时,使用对象是最好的选择。

    类支持继承,但模块不支持。

    用最简单的方式解决问题。 使用字典、列表和元组往往要比使用模块更加简单、简洁且
    快速。而使用类则更为复杂

     

  • 相关阅读:
    Java实现 LeetCode 324 摆动排序 II
    Java实现 LeetCode 324 摆动排序 II
    ubuntu 10.10配置NFS网络共享步骤
    让C/C++程序一次编译可以发布到多版本Linux之上
    linux下so动态库一些不为人知的秘密(中二)
    linux下so动态库一些不为人知的秘密(中)
    linux下so动态库一些不为人知的秘密(上)
    tftp使用方法
    Linux操作系统下三种配置环境变量的方法
    Linux中环境变量文件及配置
  • 原文地址:https://www.cnblogs.com/bawu/p/8065768.html
Copyright © 2020-2023  润新知