·类(2)

@ 继承(inheritance)


什么是继承:

B继承A:A是父类(超类),B是子类(基类)。继承可以实现代码重复利用,实现属性和方法继承。

继承可以使子类拥有父类的属性和方法,也可以重新定义某些属性、重写某些方法,即覆盖父类原有的属性和方法,使其获得父类不同的功能。当然,也可以在子类中新设置属性和方法。从技术上看,OOP里继承最主要的用途是实现多态,对于多态而言,最重要的是接口的继承性(属性和方法是否存在继承性),这是不一定的。继承也不是全为了代码的重复利用,而是为了理顺关系。

对于 Python 中的继承,前面一直在使用,那就是我们写的类都是新式类,所有新式类都是继承自 object 类。不要忘记,新式类的一种写法:

class NewStyle(object):
pass

这就是典型的继承。

继承的基本概念:

class Person:
def __init__(self,name):
self.name = name def get_name(self):
print ("hello,{}!".format(self.name)) def setHeight(self, n):
self.length = n def breast(self, n):
print ("My breast is: ",n) class Boy(Person):
def setHeight(self):
print ("The height is:1.80m .") j = Boy('jimmy')
j.get_name()
j.setHeight()
j.breast(90)
打印结果:
hello,jimmy!
The height is:1.80m .
My breast is: 90

首先,定义了一个父类 Person,定义一个子类 Boy,Boy类的括号中是Person,这就意味着 Boy 继承了 Person,Boy 是 Person 的子类,Person 是 Boy 的父类。那么 Boy 就全部拥有了 Person 中的方法和属性。

如果 Boy 里面有一个和 Person 同样名称的方法,那么就把 Person 中的同一个方法遮盖住了,显示的是 Boy 中的方法,这叫做方法的重写。实例化类 Boy之后,执行实例方法  j.setHeight(),由于在类 Boy中重写了 setHeight 方法,那么 Person 中的那个方法就不显作用了,在这个实例方法中执行的是类 Boy 中的方法。

虽然在类 Boy 中没有看到 get_name() 方法,但是因为它继承了 Person,所以 j.get_name() 就执行类 Person 中的方法。同理  j.breast(90) ,它们就好像是在类 Boy 里面已经写了这两个方法一样。既然继承了,就可以使用。

多重继承:

子类继承多个父类:

class Person:
def eye(self):
print("two eyss")
def breast(self,n):
print("the breast is:",n)
class Girl:
age = 18
def color(self):
print("the girl is white.")
class HotGirl(Person,Girl):
pass

在类的名字后面的括号中把所继承的两个类的名字写上,HotGirl 就继承了Person 和 Girl这两个类。实例化类 HotGirl,既然继承了上面的两个类,那么那两个类的方法就都能够拿过来使用。在类 Girl 中, age = 28,在对 HotGirl 实例化之后,因为继承的原因,这个类属性也被继承到 HotGirl 中,因此通过实例得到它。

继承的特点,即将父类的方法和属性全部承接到子类中;如果子类重写了父类的方法,就使用子类的该方法,父类的被遮盖。

多重继承的顺序:

如果一个子类继承了两个父类,并且两个父类有同样的方法或者属性,那么在实例化子类后,调用那个方法或属性,是属于哪个父类的呢?造一个没有实际意义,纯粹为了解决这个问题的程序:

class K1(object):
def foo(self):
print("K1-foo")
class K2(object):
def foo(self):
print("K2-foo")
def bar(self):
print("K2-bar")
class J1(K1, K2):
pass
class J2(K1, K2):
def bar(self):
print("J2-bar")
class C(J1, J2):
pass print(C.__mro__)
m = C()
m.foo()
m.bar()
打印结果:
(<class '__main__.C'>, <class '__main__.J1'>, <class '__main__.J2'>, <class '__main__.K1'>, <class '__main__.K2'>, <class 'object'>)
K1-foo
J2-bar

print C.__mro__打印出类的继承顺序。

从上面清晰看出来了,如果要执行 foo() 方法,首先看 J1,没有,看 J2,还没有,看 J1 里面的 K1,有了,即 C(没有)==>J1(没有)==>J2(没有)==>K1(找到了);bar() 也是按照这个顺序,在 J2 中就找到了一个。

这种对继承属性和方法搜索的顺序称之为“广度优先”。新式类用以及 Python3.x 中都是按照此顺序原则搜寻属性和方法的。

但是,在旧式类中,是按照“深度优先”的顺序的。因为后面读者也基本不用旧式类,所以不举例。

@ super函数


对于初始化函数的继承,跟一般方法的继承不同:

class Person:
def __init__(self):
self.height = 160
def about(self, name):
print("{} is about {}".format(name, self.height))
class Girl(Person):
def __init__(self):
self.breast = 90
def about(self, name):
print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)) cang = Girl()
cang.about("wangguniang")
打印结果:
Traceback (most recent call last):
  File "test1.py", line 14, in <module>
    cang.about("wangguniang")
  File "test1.py", line 11, in about
    print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast))
AttributeError: 'Girl' object has no attribute 'height'

在上面这段程序中,类 Girl 继承了类 Person。在类 Girl 中,初始化设置了 self.breast = 90,由于继承了 Person,按照前面的经验,Person 的初始化函数中的 self.height = 160 也应该被 Girl 所继承过来。然后在重写的 about 方法中,就是用 self.height。

实例化类 Girl,并执行 cang.about("wangguniang"),试图打印出一句话 wangguniang is a hot girl, she is about 160, and her bereast is 90。保存程序,运行报错!

信息显示 self.height 是不存在的。也就是说类 Girl 没有从 Person 中继承过来这个属性。

在 Girl中发现, about 方法重写了,__init__方法,也被重写了。它跟类 Person 中的__init__重名了,也同样是重写了那个初始化函数。这是因为在子类中重写了某个方法之后,父类中同样的方法被遮盖了。

使用super 函数:

class Person:
def __init__(self):
self.height = 160
def about(self, name):
print("{} is about {}".format(name, self.height))
class Girl(Person):
def __init__(self):
super().__init__()
self.breast = 90
def about(self, name):
print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)) cang = Girl()
cang.about("wangguniang")
打印结果:
wangguniang is a hot girl, she is about 160, and her breast is 90

在子类中,__init__方法重写了,为了调用父类同方法,使用 super(Girl, self).__init__()的方式。super 函数的参数,第一个是当前子类的类名字,第二个是 self,然后是点号,点号后面是所要调用的父类的方法。同样在子类重写的 about 方法中,也可以调用父类的 about 方法。

最后要提醒注意:super 函数仅仅适用于新式类。

@ 绑定方法与非绑定方法


要通过实例来调用类的方法(函数),经常要将类实例化。方法是类内部定义函数,只不过这个函数的第一个参数是 self。(可以认为方法是类属性,但不是实例属性)。必须将类实例化之后,才能通过实例调用该类的方法。调用的时候在方法后面要跟括号(括号中默认有 self 参数,可以不写出来)。通过实例调用方法,称这个方法绑定在实例上。

 调用绑定方法:
class Person(object):
def foo(self):
pass
pp = Person() #实例化
print(pp.foo()) #调用方法

这样就实现了方法和实例的绑定,于是通过 pp.foo() 即可调用该方法。

调用非绑定方法:

class Person:
def __init__(self):
self.height = 160
def about(self, name):
print("{} is about {}".format(name, self.height))
class Girl(Person):
def __init__(self):
super().__init__()
self.breast = 90
def about(self, name):
print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)) cang = Girl()
cang.about("wangguniang")
打印结果:
wangguniang is a hot girl, she is about 160, and her breast is 90

在子类 Girl 中,因为重写了父类的__init__方法,如果要调用父类该方法,在上节中不得不使用 super().__init__()调用父类中因为子类方法重写而被遮蔽的同名方法。

其实,非绑定方法,因为在子类中,没有建立父类的实例,却要是用父类的方法。对于这种非绑定方法的调用,还有一种方式。不过这种方式现在已经较少是用了,因为有了 super 函数。为了方便读者看其它有关代码,还是要简要说明。

例如在上面代码中,在类 Girl 中想调用父类 Person 的初始化函数,则需要在子类中,写上这么一行:Person.__init__(self)

这不是通过实例调用的,而是通过类 Person 实现了对__init__(self)的调用。这就是调用非绑定方法的用途。但是,这种方法已经被 super 函数取代,所以,如果在编程中遇到类似情况,推荐使用 super 函数。

@ 静态方法和类方法


已知,类的方法第一个参数必须是 self,并且如果要调用类的方法,必须将通过类的实例,即方法绑定实例后才能由实例调用。如果不绑定,一般在继承关系的类之间,可以用 super 函数等方法调用。

这里再介绍一种方法,这种方法的调用方式跟上述的都不同,这就是:静态方法类方法

class StaticMethod:
@staticmethod
def foo():
print("This is static method foo().") class ClassMethod:
@classmethod
def bar(cls): #类方法中要有cls参数
print("This is class method bar().")
print("bar() is part of class:", cls.__name__) static_foo = StaticMethod() #实例化
static_foo.foo() #实例调用静态方法
StaticMethod.foo() #通过类来调用静态方法
print("********")
class_bar = ClassMethod()
class_bar.bar()
ClassMethod.bar()
打印结果:
This is static method foo().
This is static method foo().
********
This is class method bar().
bar() is part of class: ClassMethod
This is class method bar().
bar() is part of class: ClassMethod

在 Python 中:

@staticmethod表示静态方法

@classmethod表示类方法

先看静态方法,虽然名为静态方法,但也是方法,所以,依然用 def 语句来定义。需要注意的是文件名后面的括号内,没有self,这和前面定义的类中的方法是不同的,也正是因着这个不同,才给它另外取了一个名字叫做静态方法。如果没有 self,那么也就无法访问实例变量、类和实例的属性了,因为它们都是借助 self 来传递数据的。

在看类方法,同样也具有一般方法的特点,区别也在参数上。类方法的参数也没有 self,但是必须有 cls 这个参数。在类方法中,能够访问类属性,但是不能访问实例属性。

简要明确两种方法。下面看调用方法。两种方法都可以通过实例调用,即绑定实例。也可以通过类来调用,即 StaticMethod.foo() 这样的形式,这也是区别一般方法的地方,一般方法必须用通过绑定实例调用。

end~
 
****** 几米花的Python ****** 博客主页:https://www.cnblogs.com/jimmy-share/  欢迎转载 ~

Pyhton-类(2)的更多相关文章

  1. pyhton类集成

    class SchoolMember:   def __init__(self,name,age):     self.name = name     self.age = age     print ...

  2. Java类的继承与多态特性-入门笔记

    相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...

  3. Python爬虫从入门到放弃(十六)之 Scrapy框架中Item Pipeline用法

    当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的python类,负责接收到item并通过它执行一些行为, ...

  4. 6-----Scrapy框架中Item Pipeline用法

    当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的python类,负责接收到item并通过它执行一些行为, ...

  5. scrapy框架中Item Pipeline用法

    scrapy框架中item pipeline用法 当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的pyt ...

  6. 爬虫(十三):scrapy中pipeline的用法

    当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的python类,负责接收到item并通过它执行一些行为, ...

  7. Python之爬虫(十八) Scrapy框架中Item Pipeline用法

    当Item 在Spider中被收集之后,就会被传递到Item Pipeline中进行处理 每个item pipeline组件是实现了简单的方法的python类,负责接收到item并通过它执行一些行为, ...

  8. Python逆向爬虫之scrapy框架,非常详细

    爬虫系列目录 目录 Python逆向爬虫之scrapy框架,非常详细 一.爬虫入门 1.1 定义需求 1.2 需求分析 1.2.1 下载某个页面上所有的图片 1.2.2 分页 1.2.3 进行下载图片 ...

  9. 《Pyhton语言程序设计》_第7章_对象和类

    #7.2.1_定义类 一个类的功能:数据域.定义方法.初始化程序 初始化程序总是被命名为:_ _init_ _ (两个连续的下划线) #7.2.4_self参数 #self参数是指向对象本身的参数,那 ...

  10. day27 Pyhton 面向对象02 类和对象的命名空间

    一.内容回顾 类:具有相同属性和方法的一类事务 # 描述一类事务轮廓的一个机制 #商品/用户/店铺 对象/实例: 对象(实例)就是类的实例化 # 对象就是类的一个具体的表现 #某一件特定的商品/某个人 ...

随机推荐

  1. Recursive functions and algorithms

    http://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursive_functions_and_algorithms A commo ...

  2. Linux动态链接库.so文件的创建与使用

    1. 介绍         使用GNU的工具我们如何在Linux下创建自己的程序函数库?一个"程序函数库"简单的说就是一个文件包含了一些编译好的代码和数据,这些编译好的代码和数据可 ...

  3. vSan中见证组件witness详解

    witness在vSan中作为见证组件其作用类似于WinServer中的仲裁磁盘,当Cluster中某一节点发生故障时,来判断该节点上的对象在哪一个新的节点上继续承载.此处需要强调的是,witness ...

  4. 第六次作业——Excel制作工资表

  5. ZT 设计模式六大原则(2):里氏替换原则

    设计模式六大原则(2):里氏替换原则 分类: 设计模式 2012-02-22 08:46 23330人阅读 评论(41) 收藏 举报 设计模式class扩展string编程2010 肯定有不少人跟我刚 ...

  6. Scala模式匹配和样例类

    Scala有一个十分强大的模式匹配机制,可以应用到很多场合:如switch语句.类型检查等.并且Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配. 1.字符匹配     def mai ...

  7. 021.4 IO流——字节、字符桥梁(编码解码)

    默认使用的就是gbk编码,这里的例子改成了utf8编码 写入—编码 private static void writeText() throws IOException { FileOutputStr ...

  8. [零基础学JAVA]Java SE面向对象部分.面向对象基础(05)

    1.继承 2.多态 3.final 4.重载与覆写 5. this/super 6.抽象类 7.接口 java: class Person{ private String name;    priva ...

  9. Spring Cloud(中文版)

    原文链接:Spring Cloud I.云原生应用 Spring Cloud上下文:应用上下文服务 2.1.Bootstrap应用程序上下文 2.2.应用程序上下文层次结构 2.3.更改Bootstr ...

  10. 28、springboot整合RabbitMQ(2)

    1.监听 1.1.监听队列 如订单系统和库存系统 订单系统下订单之后将消息存放在消息队列中 库存系统需要时刻进行监听消息队列的内容,有新的订单就需要进行库存相关的操作   此时模拟监听消息队列中的Bo ...