查看原文
其他

Java帝国对Python的渗透能成功吗?

码农翻身刘欣 Python大本营 2019-03-31


作者 | 刘欣,前IBM架构师,用15年的技术工作经验去总结提炼,以故事讲解技术本质,让大家看过以后有一种“原来如此”的感觉。

来源 |  码农翻身(公众号id:coderising)

责编 | Jane


引子


Java 帝国已经成立 20 多年,经过历代国王的励精图治,可以说是地大物博,码农众多。 


可是国王依然不满足,整天想着如何继续开拓疆土, 这一天晚上他又把几个重臣招来商议了。 


IO大臣说:“陛下,现在天下大势初定,我 Java 帝国已经占据了后端开发,大数据,Android 开发等重要地盘,再想拓展殊为不易!” 


 “是啊, 前端被 JavaScript 所盘踞,我们很难渗透啊。” 线程大臣补充。 


国王点点头,这话不错,JavaScript 一统前端,Flash 消失了,Applet 不见了。想到 Applet,国王就一阵心痛,当时 Java 是靠了 Applet 才引起码农的关注,一炮走红,后来怎么就不行了呢?


“那人工智能呢?” 国王狠狠地问道。 


“陛下明鉴,人工智能底层都是 C/C++ 的地盘,应用层被 Python 等所侵占。 ”  JDBC大臣回答。 


“云计算呢?” 


“似乎是 Go 语言的地盘。” 


 “嘶——” 国王觉得有点牙疼。 


IO大臣赶紧为君主分忧:“陛下,现在群雄涿鹿,八分天下,边境战火连年不息,陛下不仅维持住祖宗的基业,还有不小的扩展,已经是一代圣主了,不过多年征战,民力维艰,老臣有一计,也许能换来奇效。” 


 “爱卿快讲!” 


“老臣以为不战而屈人之兵才是上策,作为世界最强之王国,不仅要武力上震慑群雄,更要输出我堂堂 Java 帝国之文化和价值观。” 


 “什么文化和价值观?” 


“首先我们要大肆宣扬静态语言的种种好处,比如编译期检查发现错误,代码适合阅读和维护,适合大规模团队合作,口号我都想好了,就叫‘动态一时爽,重构火葬场!’ ” 


“嗯,这口号不错!” 国王赞许,“爱卿真是老成谋国。”


“可是有些语言也是静态的啊!你怎么宣传?” 老对头线程大臣发难。 


“陛下您想想,我们有很多宝贝,” IO大臣根本不理线程大臣, 继续侃侃而谈:“ 比如 IoC, AOP,反射、动态代理、泛型、注解、JDBC、JMS...... 还有我们引以为豪的 JVM。这些东西,那些国家可不一定有,我们派出传教士,把这些东西灌输给他们的臣民,让他们体会到 Java 的种种好处,慢慢地就把他们给同化了!到时候他们的码农自然而然就会加入我 Java 帝国。”


“陛下万万不可,不同的语言有不同的特点,我们的文化别人是接受不了的,到时候只会引起群雄耻笑。” 线程大臣觉得 IO大臣脑洞大开,简直是胡闹,非要误国不可。 


“可以一试嘛!” 国王牙不疼了,“此事由 IO大臣全权负责,一年后看效果。”


泛型


作为被派往 Python 王国的传教士,吉森带着 IO大臣的重托,风尘仆仆,终于来到了 Python 王国。 


IO大臣在挑选人选的时候,有个重要的原则:是 Java 的死忠粉丝,最好是对其他语言根本不了解,省得思想被污染。 这吉森就是其中的佼佼者。


吉森先找到一个地方安顿下来,然后边四处闲逛, 他惊奇地发现,这里类方法中的 self 满天飞, 还有强制代码缩进,果然和我大 Java 不同,颇有异域风情。  


前面是个茶馆,人声鼎沸, 吉森走了进去,看看能不能牛刀小试,宣扬一下 Java 的文化。 


 “小二,我观察了半天,你们这里怎么没有讨论泛型啊?” 吉森拉住上茶的店小二。 


 “泛型? 那是什么东西? ” 小二大惑不解。  


“你肯定是个外乡人,不是来自C++就是 Java,我说得没错吧?” 旁边不知道什么时候来了一个老头儿。 


“老先生眼光不错,我确实从 Java 王国来,我很纳闷,这里怎么没有泛型啊,据我所知, 泛型可以在编译期做类型检查,码农们在写代码的时候也不需要做类型的转换,非常好用的啊。”  吉森开始灌输Java的种种好处。 


List<String> files = new ArrayList<String>(); 

String file = files.get(0);  //不必做强制类型转换 

files.add(new File(.....)) ;  // 编译错误  


“外乡人,我们 Python 中的变量是不需要声明类型的,不会做编译期类型检查,只有在运行时才会检查这个变量到底是什么类型,能否调用它的方法, 你说,我们要泛型有什么用?” 


吉森大惊,卧槽,这 IO大臣怎么没告诉自己啊, 人家根本就没这个需求! 


想想我大 Java费了那么大劲儿去实现泛型,没想到在 Python 这里完全无用武之地, 还输出什么文化! 传什么教!


反射


吉森觉得有点被 IO大臣坑了! 不过多年的历练只是让他稍微慌乱,他马上稳住神,转移话题: “先生所言极是,晚生还有一个问题,这 Python 能支持反射吗? ” 


在 Java 王国,人们经常通过反射的方式来获取一个类的属性,方法,然后根据一个字符串的名称来调用某个类的方法。 


比如有个 url :   /user?action=login 


系统根据约定解析它,确定类是 User, 方法是 login 。 然后就可以把User对象创建起来,通过反射调用 login 方法。 


public class User{ public void login(...){ ...... }}


“哈哈哈,你这个外乡人啊,你知道为什么我们 Python 是动态语言吗? 我们 Python 的反射功能不知道比你们 Java强到哪里去了!来来来,我给你看个例子。” 


class User: def login(self): print("this is login")


“现在我打印他所有的方法:”  


methods = [x for x, y in User.__dict__.items() if type(y) == FunctionType]
print(methods) #输出 ['login']


“接下来我通过反射调用 login 方法, 老夫很久没写代码了,可能不太严谨,你明白意思就行。”


url = "/user?action=login"#从url解析得到类和action,代码略clz = "User" action = "login"
#根据名称获得User对象和方法user = globals()[clz]()func = getattr(user,action) #获取login方法func() #输出This is login


吉森看到这么寥寥几行代码,就实现了基本的反射, 真是灵活啊,这Python真是不错,他都有点羡慕了。


动态代理


不, 我肩负 IO大臣的重托, 我是来传教的,不能让这老头儿给洗脑了! 


吉森想起来了一个大杀器:动态代理,这可是 Java 的一个非常基础的技术,可以在运行时实现类和方法的增强,比如在调用业务方法的前后加上事务管理,日志管理等功能,没有动态代理,AOP 就别想了。



吉森说道:“老先生,Python 怎么去实现动态代理啊?”


老头儿微微一笑:“Java Class 有个缺点, 一旦被装入 Java 虚拟机,就没法修改了,想对他做增强,只能修改字节码创建新的类,对老的类做封装,就是代理。但是 Python 是个动态语言,在运行时就可以修改啊,比如我可以动态地给 User 类增加一个新的属性,这一点你的 Java 做不到吧?”


setattr(User,"name",'andy')print(user.name) #andy


吉森看得目瞪口呆,这真是颠覆了自己从小养成的世界观: 一个类在运行期是不能改变的,更不可能去增加什么属性。


老头儿又接着说:“你看看这个 User 类,和 Proxy 类,每次调用 login 方法的时候,我都可以动态地创建一个新的方法出来,在这个新的方法中,就可以做各种手脚了。”


class User: def login(self): print('user login') def logout(self): print('user logout') class Proxy:
def __init__(self, target): self.target = target
def __getattribute__(self, name): target = object.__getattribute__(self, "target") attr = object.__getattribute__(target, name)
if name == 'login' : def newFunc(*args, **kwargs): print ("login start") result = attr(*args, **kwargs) print( "login end")                return result return newFunc else : return attr

u = User()p = Proxy(u) p.login() #实际上调用的是动态创建的方法p.logout() #调用的是原来的方法


“你那个 Proxy 中的 __getattribute__ 是什么东西啊? ” 吉森看到魔法都在这里,不由得发问。


“每当你去调用一个方法(如login/logout),或者访问一个字段的时候,Python 都会通过 __getattribute__ 先找到这个方法或者字段,然后才是真正的调用。”


“奥,原来如此,你通过 __getattribute__ 做了手脚,如果名称是 login,就创建新的方法,在新的方法中除了调用老方法之外,还输出了日志。”


“不错,孺子可教!”


吉森现在是真心佩服动态语言了,在Java中必须得在运行时通过操纵字节码来增强,字节码啊,那可不是一般人能玩的。这Python居然在源码级别就把功能给增强了!


锦囊妙计


吉森有点怀疑自己此次 Python 王国之行的效果了,这可如何是好? 怎么回去向 IO大臣复命? 当初可是立下军令状的!


他突然想起临行前,IO大臣曾经送给自己三个锦囊,嘱咐自己只有到了最危急的时刻才能打开,现在不开,更待何时?


往怀中一摸,就发现锦囊只剩下了两个,丢了一个,这回去估计要杀头, 管不了那么多了, 吉森迅速掏出一个,只见上面写着一段话: GIL  (全局解释锁), GIL 是 Python 的命门, 这把超级大锁只允许一个线程获得 Python 解释器的控制权, 简单来说,同一时刻,只有一个线程能运行!


没想到老头儿淡淡一笑:“Python 确实有 GIL,可是这程序的瓶颈啊,它不在 CPU, 而在于 IO, 就是用户的输入,数据库的查许,网络的访问, 线程等到有 IO操作的时候,放弃 GIL 这个超级大锁,让别的线程去执行就是了。再说了,你真想利用多核的时候可以用多个进程啊!”


第一个锦囊妙计被轻松化解, 吉森赶紧掏出第二个,上面几个字:“动态一时爽,重构火葬场。”


“哈哈哈,”老头儿狡黠地笑了起来:“这都是不了解情况的外人的误解,听说过 Quora 没有? 奥,上不了是吧! 这 Quora 就是 Python 写的,人家那测试用例写得非常充分,重构也不怕! 所以啊关键是测试用例!”


第二个妙计又被化解, 吉森彻底没辙了。


真相大白


看到吉森的神色变化,老头儿开始表明身份:“实不相瞒,老夫乃是 Python国王的特使,我们的探子早就听说你们 Java 那什么破传教士计划了,你一进入我国,就被盯上了,国王特地派我来,看看能不能说服你,留在我国。”


吉森想想,回去也无法交差,这 Python 似乎还不错,进退两难之际,不妨先妥协,以图将来,于是点点头答应了。


一年以后, IO大臣开始盘点传教士计划,发现回来复命的寥寥无几,尤其是去 Python 王国的吉森, 他怎么一点消息都没有呢? 是时候再派一个人去了......


--【完】--


精彩推荐


推荐阅读:

  


Python大本营“号内搜”功能全新升级

搜索功能更强大,请在公众号菜单栏体验

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存