当前位置: 首页 > news >正文

python-静态方法和类方法

Java之类的编程语言还带有静态方法,Python类也拥有与静态方法明确对应的方法。此外,Python还拥有类方法,要比静态方法更高级一些。

静态方法与Java一样,即便没有创建类的实例,静态方法也是可以调用的,当然通过类实例来调用也是可以的。请用@staticmethod装饰器来创建静态方法,如代码清单15-1所示。

因为Python的动态性,对语言没有加太多限制,所以其继承机制要比Java和C ++等编译型语言更加简单灵活。为了了解如何在Python中使用继承,可先从本章之前讨论过的Circle类开始,再推而广之。不妨再定义一个正方形类Square:

现在,如果要在绘图程序中使用这些类,必须定义每个实例在绘图表面的位置信息。在每个实例中定义x、y坐标,即可实现这一点:

class Square:

    def __init__(self, side=1, x=0, y=0):

        self.side = side

        self.x = x

        self.y = y

class Circle:

    def __init__(self, radius=1, x=0, y=0):

        self.radius = radius

        self.x = x

        self.y = y

这种方式能起作用,但如果要扩展大量的形状类,就会产生大量重复代码,因为可能要让每种形状类都具备这种位置的概念。毫无疑问,这正是在面向对象语言中使用继承的标准场景。不用在每个形状类中都定义变量x和y,而可以将各种形状抽象为一个通用的Shape类,并让定义具体形状的类继承自该通用类。在Python中,定义方式如下:

在Python中使用继承类通常有两个要求,在Circle类和Square类的粗体代码中可以看到这两个要求。第一个要求是定义继承的层次结构,在用class关键字定义类名之后的圆括号中,给出要继承的类即可。在上述代码中,Circle和Square都继承自Shape。第二个要求比较微妙一些,就是必须显式调用被继承类的__init__方法。Python不会自动执行初始化操作,但可以用super函数让Python找到被继承的类。初始化的工作在示例中由super().__ init__(x, y)这行代码来完成,这里将调用Shape的初始化函数,用适当的参数初始化实例。如果没有显式调用父类的初始化方法,则本例中的Circle和Square的实例就不会给实例变量x和y赋值。

可以不用super来调用Shape的__init__,而是用Shape.init(self, x, y)显式给出被继承类的名字,同样能够实现在实例初始化完毕后调用Shape的初始化函数。从长远来看,这种做法不够灵活,因为对被继承类名进行了硬编码。如果日后整体设计和继承架构发生了变化,这就可能成为问题。但在继承关系比较复杂的时候,采用super会比较麻烦。因为这两种方案无法完全混合使用,所以请把代码中采用的方案清楚地记录在文档中备查。如果方法未在子类或派生类中定义,但在父类中有定义,继承机制也会生效。为了查看这种继承的效果,请在Shape类中再定义一个move方法,表示移动到指定位置。该方法将会把实例的x和y坐标修改为参数指定的值。Shape的定义现在变成了:

class Shape:

    def __init__(self, x, y):

        self.x = x

        self.y = y

    def move(self, delta_x, delta_y):

        self.x = self.x + delta_x

        self.y = self.y + delta_y

如果这个Shape定义与之前的Circle、Square一起输入完毕,就可以进行以下的交互式会话:

>>> c = Circle(1)

>>> c.move(3, 4)

>>> c.x

3

>>> c.y

4

如果在交互式会话中执行上述代码,请务必在新定义的Shape类之后将Circle类的代码重新录入一遍。以上示例中的Circle类本身没有定义move方法,但由于继承自实现move的类,因此Circle的所有实例都可以使用move方法。用比较传统的OOP术语来描述,就是所有Python方法都是虚方法。也就是说如果方法在当前类中不存在,则会在父类中逐级搜索,并采用第一个找到的方法。

实例可以继承类的属性。实例变量是和对象实例关联的,某个名称的实例变量在一个实例中只会存在一个。看一下以下示例,这里会用到以下类的定义,

class P:

    z = "Hello"

    def set_p(self):

        self.x = "Class P"

    def print_p(self):

        print(self.x)

class C(P):

    def set_c(self):

        self.x = "Class C"

    def print_c(self):

        print(self.x)

执行以下代码:

>>> c = C()

>>> c.set_p()

>>> c.print_p()

Class P

>>> c.print_c()

Class P

>>> c.set_c()

>>> c.print_c()

Class C

>>> c.print_p()

Class C

上述示例中的对象c是类C的实例。C继承自P,但c并非继承自类P的某个不可见的实例,而是直接从P继承方法和类变量的。因为只存在一个实例c,在c的方法调用中,对实例变量x的任何引用都只能指向c.x。在c上无论调用哪个类定义的方法,均会如此。如上所示,由c调用的set_p和print_p,都是在类P里定义的,且都引用了同一个变量,在c上调用set_c和print_c时,引用的也是这个变量。通常这正是实例变量应有的表现,因为对同一个名称的实例变量的引用,就应该指向同一个变量。不过有时也会有不同需求,可通过私有变量来实现(参见15.9节)。类变量是支持继承的,但应该避免命名冲突,并小心类变量一节中提及的种种现象。在以下示例中,父类P中定义了类变量z,并且通过以下3种方式都能被访问到:实例c、派生类C或直接用父类P:

>>> c.z; C.z; P.z

'Hello'

'Hello'

'Hello'

但如果通过类C来对类变量z赋值,就会在类C中创建一个新的类变量。这对P的类变量本身(通过P访问)没有影响。但以后通过类C或其实例c看到的,将会是这个新的变量,而不是原来的变量:

>>> C.z = "Bonjour"

>>> c.z; C.z; P.z

'Bonjour'

'Bonjour'

'Hello'

如果通过实例c来对z赋值,同样也会创建一个新的实例变量,最终会得到3个不同的变量

>>> c.z = "Ciao"

>>> c.z; C.z; P.z

'Ciao'

'Bonjour'

'Hello'

相关文章:

  • 蓝桥杯训练题目(一)—— 难度:简单(除了最后一题哈)
  • 《Python实战进阶》专栏 No 4:高效、简洁、强大之使用 FastAPI 构建高性能异步API
  • 请简述一下Prefab(预制体)的本质是什么?
  • 大白话TypeScript第三章高级特性学习
  • 使用Socket编写超牛的http服务器和客户端(一)
  • L1-6 剪切粘贴(字符串增删查改)
  • INTJ人格的本质
  • 【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter21-错误处理与调试
  • Linux 命令大全完整版(12)
  • apply的用法
  • 自动驾驶中planning为什么要把横纵向分开优化?
  • AI知识架构之RAG
  • 如何在VMware虚拟机的window10系统中安装网易mumu模拟器
  • Linux权限(一)
  • 【Java】求绝对值
  • hive开窗函数边界值ROWS BETWEEN 和 RANGE BETWEEN区别
  • 【带你 langchain 双排系列教程】6.LangChain多模态输入与自定义输出实战指南
  • 机器学习数学通关指南——链式法则
  • Three.js轮廓线、边框线、选中效果实现的几种实现方法以及性能评估
  • Lock-Free环形队列C++实现
  • “70后”通化市委书记孙简已任吉林省政府领导
  • 弘扬 “上海精神”,上合组织政党论坛聚焦政党责任与使命
  • 阿联酋启动第三届全球航空奖评选,奖金总额达百万美元
  • 航天科技集团质量技术部部长严泽想升任集团副总经理
  • 韩国对华中厚板征收临时反倾销税
  • 《2025职场人阅读报告》:超半数会因AI改变阅读方向