classmethod 继承_让人眼花缭乱的类继承

Python语言的一个优势是简洁易用。是否简洁易用仅仅是Python语言本身的一个话题,但“好消息”是如果你想创造那种一大堆继承、混乱的内部关系的代码,也是可以的!

今天烦人的代码来自于验证某些math-y数学分析代码。一开始,他们是发现文档和代码对应不上,只得去阅读代码看看代码到底做了什么事情。在一个文件中,找到了一个业务关注的核心类,定义如下所示。

class WeibullFitter(KnownModelParametricUnivariateFitter):
# snip: some math

表面上看,这个数学方法看上去多少是对的,但有个问题是:父类是怎么调用的?没办法,只能往上查看其父类,父类的代码如下:

class KnownModelParametricUnivariateFitter(ParametricUnivariateFitter):

_KNOWN_MODEL = True

这个所谓的基类压根就算不上基类,“鸡肋”还差不多!仅仅是设置了一个属性为True,沿着继承树再往上走,找到基类代码如下:

class ParametricUnivariateFitter(UnivariateFitter):

# ...snip...

呃,虽然这还不是最终的基类,但至少这个类还实现了某些方法。这样的写法肯定让你怀疑代码结构的问题了,目前为止还没找到真正烦人的代码。但目前我们可以讨论一下为什么说继承在某种程度上是有害的。继承会自动产生依赖,这意味着如果要理解子类的行为必须同时了解父类的行为。当然,好的继承的实现会划定这些边界,但显然我们看到的是一个反面例子。

除此之外,由于Python是一个弱类型语言,因此继承的优点之一的多态在Python里都算不上优点了。没错,我们可以通过Python的类型注解去做类型检查,这会让多态派上用场,但这没法判断整个继承树。

撇开这一切不谈,使用继承的主要原因是我们可以将公共的业务逻辑部分抽离开,让子类系统无需处理这些公共的业务逻辑。因此,即便存在多种可能的类型,我们仍然可以调用具体实例的某个方法,并且能够保证如期望那样运行,且可以呈现不同的行为。接着来看ParametricUnivariateFitter这个类,类中定义了如下方法:

def _fit_model(self, Ts, E, entry, weights, show_progress=True):

if utils.CensoringType.is_left_censoring(self): # Oh no.
negative_log_likelihood = self._negative_log_likelihood_left_censoring
elif utils.CensoringType.is_interval_censoring(self): # Oh no no no.
negative_log_likelihood = self._negative_log_likelihood_interval_censoring
elif utils.CensoringType.is_right_censoring(self): # This is exactly what I think it is isn't it.
negative_log_likelihood = self._negative_log_likelihood_right_censoring

# ...snip...

注释是问题发现人提供的。为了满足整个子类树,每个子类都使用了类型检查,因此子类不同的行为是通过类型检查来实现的。这可以说是100%的臭代码!当我们去阅读CensoringType代码的时候,让我们再次确信了这一点。

class CensoringType(Enum): # enum.Enum from the standard library
LEFT = "left"
INTERVAL = "interval"
RIGHT = "right"

@classmethod
def right_censoring(cls, function: Callable) -> Callable:
@wraps(function) # functools.wraps from the standard library def f(model, *args, **kwargs):
cls.set_censoring_type(model, cls.RIGHT)
return function(model, *args, **kwargs)

return f

@classmethod
def left_censoring(cls, function: Callable) -> Callable:
@wraps(function)
def f(model, *args, **kwargs):
cls.set_censoring_type(model, cls.LEFT)
return function(model, *args, **kwargs)

return f

@classmethod
def interval_censoring(cls, function: Callable) -> Callable:
@wraps(function)
def f(model, *args, **kwargs):
cls.set_censoring_type(model, cls.INTERVAL)
return function(model, *args, **kwargs)

return f

@classmethod
def is_right_censoring(cls, model) -> bool:
return cls.get_censoring_type(model) == cls.RIGHT

@classmethod
def is_left_censoring(cls, model) -> bool:
return cls.get_censoring_type(model) == cls.LEFT

@classmethod
def is_interval_censoring(cls, model) -> bool:
return cls.get_censoring_type(model) == cls.INTERVAL

@classmethod
def get_censoring_type(cls, model) -> str:
return model._censoring_type

@classmethod
def str_censoring_type(cls, model) -> str:
return model._censoring_type.value

@classmethod
def set_censoring_type(cls, model, censoring_type) -> None:
model._censoring_type = censoring_type

即便是你不懂Python代码,你也会想到这是一个枚举。@classmethod是Python注解静态方法的修饰符,就像类成员方法中使用self作为第一个参数一样,静态方法使用cls作为方法的第一个参数,以代表类本身。

去看一下right_censoring方法也很重要,因为这些方法看起来是“装饰器”。他们使用了@wraps来修饰定义的局部方法。这个right_censoring方法需要接收一个可调用的函数(也许是一个构造函数),然后将该方法的实现用内部以“f”方法替换。并且在这里面,在调用构造函数之前,修改了构造方法的参数值。

如果你不经常使用Python编程的话,你可能会觉得十分困惑,因此我们来看看这个方法如何使用的:

@CensoringType.right_censoringclass SomeCurveFitterType(SomeHorribleTreeOfBaseClasses):
def __init__(self, model, *args, **kwargs):
# snip
instance = SomeCurveFitterType(model, *args, **kwargs)

在最后一行代码,并没有调用```init``构造函数,而是首先通过内部的f函数,这个函数最重要的一件事就是在调用构造函数前,调用静态方法cls.set_censoring_type(model, cls.RIGHT) 。

如果你对此完全不理解,也不用感到糟糕。装饰器是Python的一种独特的方式去修改类和方法的实现。这个特性允许你在传统的方式中混合使用声明式编程。

最终,为了理解WeibullFilter这个类的行为,你必须阅读半大祖先类代码才能看到BaseFitter类型,然后你必须注意应用了什么装饰器以及装饰器对应的祖先类,只有这样才真正知道这个业务的功能是什么。

如果你是写这些代码的人,也许会觉得这种混入装饰器和继承的写法扩展性很高。可以快速轻松地在框架里插入一个曲线拟合方法。你甚至会以为你的大脑创造了这个星球上最具工程化的框架。而余下的我们,必须像盲人那样使用棍子去探路。
最后我们的问题提交人总结到:

我对此感到很糟糕。这个库看着不错,数学方法本身也不错,并且这个库非常有用,减轻了我很多工作……
这一系列的行为导致了性能问题,我不得不重新做不同的实现。因为这一系列的嵌套调用将简单的处理过程的性能给破坏了——执行时间可能超过10分钟。而新写的代码几乎是瞬间完成,包含注释也就不到20行。

print("正文结束!")

本文翻译自:thedailywtf.com

a1e1cc77698a2b668a9ef55bf5638020.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556974.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA SSM框架+Redis 实现单点登录

1:什么是单点登录? 答:单点登录的英文名叫做:Single Sign On(简称SSO) 一般我们的模块都是在同一个系统下,同一个tomcat(如图,以商城为例) 后来为了维护和…

2017云栖大会门票转让_「揭秘GP」云栖大会 | Greenplum 6.0 内核优化解读和7.0展望...

9月25日,云栖大会在杭州阿里巴巴云栖小镇正式拉开序幕,三天会议期间,共吸引了200多位世界级科学家、400多家科技合作伙伴参与,科技展区面积超过3万平方米,共发布了1000多项顶尖技术。云栖大会现场在此次云栖大会上&…

从mysql到大数据(一)--开宗明义

一、大数据长什么样 长像很普通,至少看两眼后就觉得很平常。 举个栗子: 一个表格,学生信息表,里面有学号、姓名、性别、年龄、学校、学院、专业、年级、宿舍号等信息如下, 但是表在库里,我们想看&#xf…

SSO单点登录方案大全

分布式微服务系统主流常用的登录方案 前言: 单点登录其实是一个概念,主要是为了解决一次登录,多系统(本系统或外部系统)之间不需要重复登录的问题,就目前来说,主流的解决方案针对业务场景分为3个方向: 1: 同一公司,同父域下的单点登录解决方案. 如[http://map.baidu.com][[h…

em算法怎么对应原有分类_机器学习基础-EM算法

EM算法也称期望最大化(Expectation-Maximum,简称EM)算法,它是一个基础算法,是很多机器学习领域算法的基础,比如隐式马尔科夫算法(HMM), LDA主题模型的变分推断等等。本文就对EM算法的原理做一个总结。EM算法要解决的问题我们经常会…

postman插件下载、安装教程

这里只讲如何在Chrome 中安装postman插件 下载链接:https://pan.baidu.com/s/1vampHeD0UiDNbrB3G8j_hA 提取码:wqdl 方法/步骤 1.在Chrome输入地址:[chrome://extensions/] 2.将压缩包直接拖拽至Chrome中 3.运行在Chrome输入地址&#xff…

得力条码扫描器怎么用_广东智能物流控制系统怎么选

广东智能物流控制系统怎么选,东莞智库,东莞智库(SmartWarehouse),专注电子制造SMT智能仓库,致力于帮助电子制造企业提高物流仓储效率和效能。广东智能物流控制系统怎么选, 旭日东自动分拣系统是个集机械、电气、计算机…

从mysql到大数据(三)--mysql数据库建模一常用数据类型及引擎

数据库的安装请自行百度。如果你想直捣黄龙练查询,没有表没有数据是不能实现的。我们从建表开始学习。但要知道,我们所有东西都是了解,学习不要有压力,不要必须要求记什么,当然,如果你不累可以记&#xff0…

Postman用法简介-Http请求模拟工具

Postman用法简介-Http请求模拟工具 在我们平时开发中,特别是需要与接口打交道时,无论是写接口还是用接口,拿到接口后肯定都得提前测试一下,这样的话就非常需要有一个比较给力的Http请求模拟工具,现在流行的这种工具也…

matlab多元函数_函数的计算机处理8(1)_1MATLAB

计算机语言运用--数值计算8-函数的计算机处理8(1)_1MATLAB计算机:电子线路组成的计算机器。人与计算机则是通过计算机语言-符号系统说给计算机听而交流。计算机语言有低级语言-机器语言、汇编、高级语言-C/C/C#/VB/PASCAL/LISP/JAVA/PYTHON/……成百上千种之多。 作…

java 怎么通过url获取远程服务器上某个文件夹下的所有文件_JMX远程代码漏洞研究...

前言:前一段时间apace solr JMX因为配置不当出现远程代码执行漏洞,最近自己在看一套java系统时,发现该系统也存在JMX远程代码漏洞,于是乎就想研究下JMX这种通用型漏洞,下面我就从原理到利用对该漏洞做一个简单的梳理。…

app每秒并发数_性能测试连载 (38) jmeter 线程数与性能测试的负载模式

点击跳转>>jmeter--由浅入深学性能系列需求下面有3个场景,思考一下在jmeter里面如何设计场景1:有一个项目,500用户同时登录,响应时间能达到多少场景2:考勤打卡,最大吞吐量能达到多少(每秒最大能完成多…

用自定义注解做点什么——自定义注解有什么用

用自定义注解做点什么 前言 你不一定听过注解,但你一定对Override不陌生。 当我们重写父类方法的时候我们就看到了Override。我们知道它表示父类方法被子类重写了。 现在告诉你,Override就是一个注解。 也许你会疑惑注解是什么? 注解&…

c++ 查找文件夹下最新创建的文件_云计算开发总结:搜索Linux文件和文件夹的方法...

当下,随着Linux在物联网、云技术、超级计算和人工智能等领域扮演关键角色,各种会议和新版本的发布令人应接不暇,Linux将迎来一个激动人心的“云时代”。如果你想把握这个风口,现在是学习Linux技术的最佳时期。今天千锋广州云计算培…

RSA 非对称加密原理

RSA 加密原理 步骤说明描述备注1找出质数P 、Q-2计算公共模数N P * Q-3欧拉函数φ(N) (P-1)(Q-1)-4计算公钥E1 < E < φ(N)E的取值必须是整数 E 和 φ(N) 必须是互质数5计算私钥DE * D % φ(N) 1-6加密C &#xff1d; M E mod NC&#xff1a;密文 M&#xff1a;明文7…

浅谈对称加密与非对称加密

在数字加密算法中&#xff0c;通过可划分为对称加密和非对称加密。 一&#xff1a;什么是对称加密&#xff1f; 在对称加密算法中&#xff0c;加密和解密使用的是同一把钥匙&#xff0c;即&#xff1a;使用相同的密匙对同一密码进行加密和解密&#xff1b; 加密过程如下&…

ios跨线程通知_一种基于Metal、Vulkan多线程渲染能力的渲染架构

快手Y-tech 原创最新技术干货分享随着3D渲染场景规模越来越复杂&#xff0c;单线程渲染架构在满足业务性能要求时已经捉襟见肘&#xff0c;因此&#xff0c;多线程渲染显得愈发重要。本文首先介绍了新一代图形渲染接口Metal、Vulkan&#xff0c;以及它们的多线程渲染特性&…

58同城面试盘点

58同城面试盘点 1.一张订单表&#xff0c;有user_name,order_id,order_time,order_amount 四个字段&#xff0c;怎么取出每个用户2021年10月以来第一个订单的金额&#xff08;下单时间格式为’yyyy-MM-dd HH:mm:ss’&#xff09;&#xff1f; select user_name,order_id,orde…

stringbuffer判断是否为空

StringBuffer sbnew StringBuffer();if(sb!null && sb.length()>0){System.out.println("证明sb不为空!"); }

virtualbox: win11主机安装deepin双向复制问题

virtualbox: win11主机安装deepin双向复制问题1.安装virtualbox增强组件(确保光驱可用)2.终端挂载3. 运行BoxLinuxAdditions4. 重启虚拟机&#xff0c;验证OK&#xff01;使用virtualbox安装深度系统deepin虚拟&#xff0c;发现虚拟机和宿主机之间不能双向复制&#xff0c;已经…