aspxbs 发表于 2023-10-4 18:00:25

怎么样才算是精通 Python?

当初学习 Python 的时候,看见各种招聘要求写着 “精通 Python 语言”。所以才问了这样一个问题,求教大家。过去一年多了,收到了很多回答,也看到了很多想法。

随风舞动 发表于 2023-10-4 18:01:03

这个回答可能有点长,我会先给出我对精通Python的理解,然后给出一些Python中有难度的知识点。如果大家在看完我这篇回答之前,已经充分理解了我列出的各个知识点,那么,我相信你已经算是精通Python了。如果不能,我希望这篇回答能让你意识到自己Python知识还存在哪些不足,在之后的学习中,从哪些方面去改进。
精通是个伪命题
怎样才算精通Python,这是一个非常有趣的问题。
很少有人会说自己精通Python,因为,这年头敢说精通的人都会被人摁在地上摩擦。其次,我们真的不应该纠结于编程语言,而应该专注于领域知识。比如,你可以说你精通数据库,精通分布式,精通机器学习,那都算你厉害。但是,你说你精通Python,这一点都不酷,在业界的认可度也不高。
再者,Python使用范围如此广泛,一个人精力有限,不可能精通所有的领域。就拿Python官网的Python应用领域来说,Python有以下几个方面的应用:

[*]Web Programming: Django, Pyramid, Bottle, Tornado, Flask, web2py
[*]GUI Development: wxPython, tkInter, PyGtk, PyGObject, PyQt
[*]Scientific and Numeric: SciPy, Pandas, IPython
[*]Software Development: Buildbot, Trac, Roundup
[*]System Administration: Ansible, Salt, OpenStack

如果有人声称精通上面所有领域,那么,请收下我的膝盖,并且,请收我为徒。
既然精通Python是不可能也是没有意义的事情,那么,为什么各个招聘要求里面,都要求精通Python呢?我觉得这都是被逼的。为什么这么说呢,且听我慢慢说来。
为什么招聘要求精通Python
绝大部分人对Python的认识都有偏差,认为Python比较简单。相对于C、C++和Java来说,Python是比较容易学习一些,所以,才会有这么多只是简单地了解了一点语法,就声称自己会Python的工程师。
打个比方,如果一个工程师,要去面试一个C++的岗位,他至少会找一本C++的书认真学习,然后再去应聘。Python则不然,很多同学只花了一点点时间,了解了一下Python的语法,就说自己熟悉Python。这也导致Python的面试官相对于其他方向的面试官,更加容易遇到不合格的求职者,浪费了大家的时间。Python面试官为了不给自己找麻烦,只能提高要求,要求求职者精通Python。
怎样才算精通Python
既然精通Python本身是一件不可能的事情,而面试官又要求精通Python,作为求职者,应该达到怎样的水平,才敢去应聘呢?我的观点是,要求精通Python的岗位都是全职的Python开发,Python是他们的主要使用语言,要想和他们成为同事,你至少需要:
1. 能够写出Pythonic的代码(什么是Pythonic的代码,请看我在另一个问题下的回答:
怎样才能写出pythonic的代码? - 知乎用户的回答)
2. 对Python的一些高级特性比较熟悉
3. 对Python的优缺点比较了解
这样说可能比较抽象,不太好理解。我们来看几个例子,如果能够充分理解这里的每一个例子,那么,你完全能够顺利通过"精通Python"的岗位面试。
敢来挑战吗

http://pic1.zhimg.com/50/v2-54129fa9354f11e6ed5bdebb4d394625_720w.jpg?source=1940ef5c
1.上下文管理器
大家在编程的时候,经常会遇到这样的场景:先执行一些准备操作,然后执行自己的业务逻辑,等业务逻辑完成以后,再执行一些清理操作。
比如,打开文件,处理文件内容,最后关闭文件。又如,当多线程程序需要访问临界资源的时候,线程首先需要获取互斥锁,当执行完成并准备退出临界区的时候,需要释放互斥锁。对于这些情况,Python中提供了上下文管理器(Context Manager)的概念,可以通过上下文管理器来控制代码块执行前的准备动作以及执行后的收尾动作。
我们以处理文件为例来看一下在其他语言中,是如何处理这种情况的。 Java风格/C++风格的Python代码:
    myfile= open(r'C:\misc\data.txt')
    try:
      for line in myfile:
            ...use line here...
    finally:
      myfile.close()
Pythonic的代码:
    with open(r'C:\misc\data.txt') as myfile:
      for line in myfile:
            ...use line here...
我们这个问题讨论的是精通Python,显然,仅仅是知道上下文管理器是不够的,你还需要知道:
1. 上下文管理器的其他使用场景(如数据库cursor,锁)


[*]上下文管理器管理锁


      class FetchUrls(threading.Thread):
            ...
            def run(self):
                ...
                with self.lock:   #使用"with"语句管理锁的获取和释放
                  print 'lock acquired by %s' % self.name
                  print 'lock released by %s' % self.name


[*]上下文管理器管理数据库cursor


      import pymysql

      def get_conn(**kwargs):
            return pymysql.connect(host=kwargs.get('host', 'localhost'),
                  port=kwargs.get('port', 3306),
                  user=kwargs.get('user'),
                  passwd=kwargs.get('passwd'))

      def main():
            conn = get_conn(user='laimingxing', passwd='laimingxing')
            with conn as cur:
                cur.execute('show databases')
                print cur.fetchall()

      if __name__ == '__main__':
            main()


[*]上下文管理器控制运算精度


      with decimal.localcontext() as ctx:
            ctx.prec = 22
            print(decimal.getcontext().prec)
2. 上下文管理器可以同时管理多个资源
   假设你需要读取一个文件的内容,经过处理以后,写入到另外一个文件中。你能写出Pythonic的代码,所以你使用了上下文管理器,满意地写出了下面这样的代码:
      with open('data.txt') as source:
            with open('target.txt', 'w') as target:
                target.write(source.read())
    你已经做得很好了,但是,你时刻要记住,你是精通Python的人啊!精通Python的人应该知道,上面这段代码还可以这么写:
      with open('data.txt') as source, open('target.txt', 'w') as target:
            target.write(source.read())
3. 在自己的代码中,实现上下文管理协议
    你知道上下文管理器的语法简洁优美,写出来的代码不但短小,而且可读性强。所以,作为精通Python的人,你应该能够轻易地实现上下文管理协议。在Python中,我们就是要自己实现下面两个协议:


[*]__enter__(self) Defines what the context manager should do at the beginning of the block created by the with statement. Note that the return value of __enter__ is bound to the target of the with statement, or the name after the as.
[*]__exit__(self, exception_type, exception_value, traceback) Defines what the context manager should do after its block has been executed (or terminates). It can be used to handle exceptions, perform cleanup, or do something always done immediately after the action in the block. If the block executes successfully, exception_type, exception_value, and traceback will be None. Otherwise, you can choose to handle the exception or let the user handle it; if you want to handle it, make sure __exit__ returns True after all is said and done. If you don't want the exception to be handled by the context manager, just let it happen.


2. 装饰器
由于我们这个问题的题目是精通Python,所以,我假设大家已经知道装饰器是什么,并且能够写简单的装饰器。那么,你是否知道,写装饰器也有一些注意事项呢。
我们来看一个例子:
    def is_admin(f):
      def wrapper(*args, **kwargs):
            if kwargs.get("username") != 'admin':
                raise Exception("This user is not allowed to get food")
            return f(*args, **kwargs)
      return wrapper


    @is_admin
    def barfoo(username='someone'):
      """Do crazy stuff"""
      pass

    print barfoo.func_doc
    print barfoo.__name__

    None
    wrapper我们用装饰器装饰完函数以后,无法正确地获取到原函数的函数名称和帮助信息,为了获取这些信息,我们需要使用@functool.wraps。 如下所示:
    import functools
   
    def is_admin(f):
      @functools.wraps(f)
      def wrapper(*args, **kwargs):
            if kwargs.get("username") != 'admin':
                raise Exception("This user is not allowed to get food")
            return f(*arg, **kwargs)
      return wrapper
再比如,我们要获取被装饰的函数的参数,以进行判断,如下所示:
    import functools
    def check_is_admin(f):
      @functools.wraps(f)
      def wrapper(*args, **kwargs):
            if kwargs.get('username') != 'admin':
                raise Exception("This user is not allowed to get food")
            return f(*args, **kwargs)
      return wrapper
   
    @check_is_admin
    def get_food(username, food='chocolate'):
      return "{0} get food: {1}".format(username, food)
   
    print get_food('admin')
这段代码看起来没有任何问题,但是,执行将会出错,因为,username是一个位置参数,而不是一个关键字参数,我们在装饰器里面,通过kwargs.get('username')是获取不到username这个变量的。为了保证灵活性,我们可以通过inspect来修改装饰器的代码,如下所示:
    import inspect
    def check_is_admin(f):
      @functools.wraps(f)
      def wrapper(*args, **kwargs):
            func_args = inspect.getcallargs(f, *args, **kwargs)
            print func_args
            if func_args.get('username') != 'admin':
                raise Exception("This user is not allowed to get food")
            return f(*args, **kwargs)
      return wrapper
装饰器还有很多知识,比如装饰器怎么装饰一个类,装饰器的使用场景,装饰器有哪些缺点,这些,你们都知道吗?感兴趣的同学 可以看我以前的一篇博客(
python装饰器入门与提高)
3. 全局变量
关于Python的全局变量,我们先从一个问题开始:Python有没有全局变量?可能你看到这个问题的时候就蒙圈了,没关系,我来解释一下。
从Python自己的角度来说,Python是有全局变量的,所以,Python为我们提供了global关键字,我们能够在函数里面修改全局变量。但是,从C/C++/Java程序员的角度来说,Python是没有全局变量的。因为,Python的全局变量并不是程序级别的(即全局唯一),而是模块级别的。模块就是一个Python文件,是一个独立的、顶层的命名空间。模块内定义的变量,都属于该命名空间下,Python并没有真正的全局变量,变量必然属于某一个模块。
我们来看一个例子,就能够充分理解上面的概念。三种不同的修改全局变量的方法:
    import sys
   
    import test
   
    a = 1
   
    def func1():
      global a
      a += 1
   
    def func2():
      test.a += 1
   
    def func3():
      module = sys.modules['test']
      module.a += 1
   
    func1()
    func2()
    func3()
这段代码虽然看起来都是在对全局变量操作,其实,还涉及到命名空间和模块的工作原理,如果不能很清楚的知道发生了什么,可能需要补充一下自己的知识了。
4. 时间复杂度
我们都知道,在Python里面list是异构元素的集合,并且能够动态增长或收缩,可以通过索引和切片访问。那么,又有多少人知道,list是一个数组而不是一个链表。
关于数组和链表的知识,我想大家都知道了,这里就不再赘述。如果我们在写代码的过程中,对于自己最常用的数据结构,连它的时间复杂度都不知道,我们又怎么能够写出高效的代码呢。写不出高效的代码,那我们又怎么能够声称自己精通这门编程语言呢。
既然list是一个数组,那么,我们要使用链表的时候,应该使用什么数据结构呢?在写Python代码的时候,如果你需要一个链表,你应该使用标准库collections中的deque, deque是双向链表。标准库里面有一个queue,看起来和deque有点像,它们是什么关系?这个问题留着读者自己回答。
我们再来看一个很实际的例子:有两个目录,每个目录都有大量文件,求两个目录中都有的文件,此时,用Set比List快很多。因为,Set的底层实现是一个hash表,判断一个元素是否存在于某个集合中,List的时间复杂度为O(n),Set的时间复杂度为O(1),所以这里应该使用Set。我们应该非常清楚Python中各个常用数据结构的时间复杂度,并在实际写代码的过程中,充分利用不同数据结构的优势。
5. Python中的else
最后我们来看一个对Python语言优缺点理解的例子,即Python中增加的两个else。相对于C++语言或者Java语言,Python语法中多了两个else。
一个在while循环或for循环中:
    while True:
      ....
    else:
      ....
另一个在try...except语句中:
    try:
      ....
    except:
      ....
    else:
      ....
    finally:
       ....
那么,哪一个是好的设计,哪一个是不好的设计呢?要回答这个问题,我们先来看一下在大家固有的观念中,else语句起到什么作用。在所有语言中,else都是和if语句一起出现的:
    if <condition>
      statement1
    else
      statement2
翻译成自然语言就是,如果条件满足,则执行语句1,否则,执行语句2。注意我们前面的用语,是否则,也就是说,else语句在我们固有的观念中,起到的作用是“否则”,是不满足条件的情况下才执行的。
我们来看Python中,while循环后面的else语句。这个else语句是在while语句正常结束的时候执行的。所以,按照语意来说,while循环的else起到的作用是and。也就是说,在Python中,while循环末尾的else换做and才是更加合适的。
你可能觉得我有点钻牛角尖,那好,我再强调一遍,while循环中的else语句是在循环正常结束的时候执行的,那么请问:
1. 如果while循环里面遇到了break语句,else语句会执行吗
2. 如果while循环最后,遇到了continue语句,else语句还会执行吗
3. 如果while循环内部出现异常,else语句还会执行吗
这里的几个问题,大多数人都不能够很快的正确回答出来。而我们的代码是写给人看的,不应该将大多数人排除在能够读懂这段代码之外。所以我认为,Python语言中循环语句末尾的else语句是一个糟糕的设计。
现在,我们再来看try...except语句中的else,这个else设计得特别好,其他语言也应该吸取这个设计。这个设计的语义是,执行try里面的语句,这里面的语句可能会出现异常,如果出现了异常,就执行except里面的语句,如果没有出现异常,就执行else里面的语句,最后,无论是否出现异常,都要执行finally语句。这个设计好就好在,else的语句完全和我们的直观感受是一样的,是在没有出现异常的情况下执行。并且,有else比没有else好,有了else以后,正确地将程序员认为可能出现异常的代码和不可能出现异常的代码分开,这样,更加清楚的表明了是哪一条语句可能会出现异常,更多的暴露了程序员的意图,使得代码维护和修改更加容易。
结论:我这篇回答很长,但是,我相信对很多人都会有帮助。这里想说的是,Python是一门编程语言,使用范围非常广泛,大家不要去追求精通Python程序语言自身,而应该将精力放在自己需要解决的实际问题上。其次,绝大多数人对Python的认识都存在误区,认为Python很简单,只是简单地了解一下就开始写Python代码,写出了一堆很不好维护的代码,我希望这一部分人看到我的回答以后,能够回去重新学习Python。最后,对于一些同学的疑虑——招聘职位要求精通Python,我的回答是,他们并不奢望招到一个精通Python的人,他们只是想招到一个合格的工程师,而大部分的Python工程师,都,不,合,格!
如果你喜欢我这篇回答(那就点赞以示鼓励),可能对我这篇回答也会感兴趣:
怎样才能写出pythonic的代码? - 知乎用户的回答Python 有哪些优雅的代码实现?让自己的代码更pythonic - 知乎用户的回答

llmllm 发表于 2023-10-4 18:01:23

谢邀。但是俺回答不了啊。
我在以前找工作的时候,曾经用过「精通X」之类的词。随着踩得坑越多,越用越感觉自己懂的太少,要学的太多了,越用越发现它并不是自己当初一厢情愿的认为的那么简单。
不知道每个人对「精通」的定义是什么。对我来说,中国连一个Python核心开发者都没有(见
https://zhuanlan.zhihu.com/p/22217076),我感觉这个问题还是空白答案比较好(得罪楼上好多人啊)

冰点精灵 发表于 2023-10-4 18:01:57

你们所说的什么要怎样数据分析、爬虫、Web 等,在我看来那不是精通 Python,而是精通某一领域,抛开 Python 换用另一门语言也可,在我看来,精通 Python 语言大约需要如下这些步骤(个人愚见,不服你奈我何~~~):
------------------------
level 1:了解基本语法
这是最容易的一级,掌握了 Python 的基本语法,可以通过 Python 代码实现常用的需求,不管代码质量怎么样。这部分内容,可以参考:
The Python Tutorial。
------------------------
level 2:熟练使用常用的库

[*]熟悉常用 standard library 的使用,包括但不限于 copy / json / itertools / collections / hashlib / os / sys 等,这部分内容,可以参考:The Python Standard Library。
[*]熟悉常用的第三方库,这就根据每个人不同的用法而有所不同了,但是一定要掌握你所常用的那个领域里的第三方库。
------------------------
level 3:Pythonic
这一级别比上一级别稍难,但是还是可以轻松达到。所谓 Pythonic,就是相比其它语言,Python 可以通过更加优雅的实现方式(不管是语法糖还是什么),比如(包括但不限于) with、for-else、try-else、yield 等。
另外你还需要掌握这些所谓魔法的实现原理,了解 Python 在语法层面的一些协议,可以自己实现语法糖。如 with 的实现方式(上下文管理器)等。
达到这一级,你的代码可以看起来很漂亮了。这部分内容,可以参考:

[*]The Python Language Reference
[*]Python HOWTOs
------------------------
level 4:高级玩法
掌握 Python 的内存机制、GIL 限制等,知道如何改变 Python 的行为,可以轻松写出高效的优质的 Python 代码,能够轻松分辨不同 Python 代码的效率并知道如何优化。
------------------------
level 5:看透本质
阅读 Python 的 C 实现,掌握 Python 中各种对象的本质,掌握是如何通过 C 实现面向对象的行为,对于常见的数据结构,掌握其实现细节。到这一步,需要将 Python 源码学习至少一遍,并对关键部分有较深层次的理解。
------------------------
level 6:手到拈来,一切皆空
不可说,不必说~


--------------------- 以下为补充说明 ---------------------------
看到评论区有朋友误解,这里解释一下。
首先,以上步骤并不是打怪升级,不存在到了一级再去第下一级,你完全可以在熟练标准库的过程中掌握语言的实现原理等,这并不矛盾。所以那些评论说「我已经 xx 级了, xx 时候要到 xx 级」的朋友们,希望不要误解了我的意思,你不必将自己限制死,而可以很灵活的变通。
另外题干中的「招聘要求写着“精通 Python 语言”」,对于这样的招聘要求,绝大多数其实是 HR 脑残(不排除少部分公司真的有特殊需求),一般面对这样的公司我会选择绕道而行。既然是找工作,还是要找自己满意且公司本身很不错的,人生苦短,何必跟自己过不去呢?说到这里,我看知乎的招聘岗位中(
知乎招聘),貌似没有出现「精通」二字,自认技术高超的小伙伴可以去试试看哟~~~
最后,希望各位不要被某些答案「不需要精通 xx 语言」这样的言论干扰,编程语言有很多相似的地方,个人觉得掌握一门语言的底层实现,对自身的编程水平提升是很有帮助的。但是要搞清楚,仅仅编程语言是不够的,如何使用一门编程语言做有趣的事情,这很重要。

shaoye85826 发表于 2023-10-4 18:02:39

更新:
之前在组里分享过,
后端那些事,有兴趣可以看看。
要想精通python,写的代码首先得pythonic,自己闭门造车肯定不行,肯定需要研读牛B的开源代码,在这过程中会遇到python的许多高阶用法
1.装饰器 装饰器在框架中运用的很多,比如flask_login,要精通至少很随意的写出满足需求的装饰器,用装饰器肯定需要functools模块的支持
2.生成器 说道生成器就自然地联系到各种推导式(列表,元祖,字段,集合),那肯定也要提到itertools模块,contextlib标准库中是个典范,说到contextlib就需要提到with协议,迭代器协议,以及标准库中的哪些实现了它们,如文件描述符,线程锁,继续延伸的话需要了解greenlet提供的协程,那就不得不提gevent,eventlet
3.描述符 这你就得知道所有的函数其实都是描述符,property,classmethod,staticmethod都是通过描述符实现的,那就得提到werkzeug和bottle都提供的cached_property,都是访问属性的就得提到
__getattr__和__getattribute__,知道在合适的时候定义合适的方法简化流程
4.元类 其实这玩意用处很大,如sqlalchemy,django的orm中field的定义都用到了它,那你得知道当python解析py时,发现__metaclass__的时候就会调用元类的__new__和__init__,如果你理解元类的__new__和__init__的第一个参数都是类(而不是self)那元类就差不多了
5.多线程 虽说python由于gil的限制不能利用多核,但在处理io密集型的任务还是有很大好处的,
那得知道threading.RLock是线程可重入锁,daemon thread的用处(python执行环境会等待所有非daemon thread的结束),Queue是线程安全的锁,logging是线程安全的日志模块,还有线程池也要熟悉
6.其它 如python2,6/7包含了不少新特性,如abc模块的抽象方法机制,collections提供的有用容器,python中的编码问题,super为啥需要两个参数,而3不需要参数,经典的闭包问题,NotImplemented和NotImplementedError的区别,多继承的mro问题,相对导入原理(__name__,如果看最新开源代码,基本都是相对导入)......
其实还有很多,但更多的是和业务相关,比如正则表达式,那至少得知道贪婪,非贪婪,命名组等。搞服务器开发,socket,twisted,gevent肯定要精通。搞web开发,django,flask,tornado得熟悉。。。。

abars 发表于 2023-10-4 18:02:46


[*]当你觉得它很美的时候,你入门了。
[*]当你觉得它很好用的时候,你掌握它了。
[*]当你发现原来还有很多不知道的东西时,你是高手了。
[*]当你知道这个问题没有答案时,你已经合格了。
页: [1]
查看完整版本: 怎么样才算是精通 Python?