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、何时使用类和对象而不是模块

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

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

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

 

转载于:https://www.cnblogs.com/bawu/p/8065768.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值