zoukankan      html  css  js  c++  java
  • 编写高质量代码 改善Python程序的91个建议——笔记(二)

    建议34:str()和repr()的区别

      1)两者之间的目标不同:str()主要面向用户,其目的是可读性,返回形式为用户友好性和可读性都较强的字符串类型;而repr()面向的是python解释器,或者说开发人员,期目的是准确性,其返回值表示python解释器内部的含义,常作为编程人员debug用途。

      2)在解释器中直接输入a时默认调用repr()函数,而pring(a)则调用str()函数。 

      3)repr()的返回值一般可以用eval()函数来还原对象,通常来说有如下等式:

        obj == eval(repr(obj))

      4)这两个方法分别调用内建的__str__()和__repr__()方法,一般来说在类中都应该定义__repr__()方法,而__str__()方法则可选,当可读性比准确性更为重要的时候应该考虑定义__str__()方法。如果类中没有定义__str__()方法,则默认会使用__repr__()方法的结果来返回对象的字符串表示形式。用户实现__repr__()方法的时候最好保证其返回值可以用eval()方法使对象重新还原。

     建议39:使用Counter进行计数统计

      1)使用dict进行计数统计

         2)使用defaultdict

    from collections import defaultdict
    
    some_data = ['a', '2', '4', 'b', 'b', '5', '4', 'a', 'a', '9', '0', 'c', '5']
    conut_frq = defaultdict(int)
    for item in some_data:
        conut_frq[item] += 1
    print(conut_frq)

        3)使用set和list

       使用Counter:

    from collections import Counter
    
    some_data = ['a', '2', '4', 'b', 'b', '5', '4', 'a', 'a', '9', '0', 'c', '5']
    print(Counter(some_data))

     建议40:深入掌握ConfigParser

      getboolean()函数:将配置项的值转换为布尔值,0,no,false,off都会被黑底为False;1,yes,true,on则都被黑底为True,其他值都会导致抛出ValueError异常。

      [DEFAULT]配置:当读取的配置项不在指定的节里面,ConfigParser就会到[DEFAULT]节中查找。

    [DEFAULT]
    con_str = %(dbn)s://%(user)s:%(pw)s@%(host)s:%(port)s/%(db)s
    dbn = mysql
    user = root
    host = localhost
    port = 3306
    
    [db1]
    user = aaa
    pw = ppp
    db = example
    
    [db2]
    host = 192.168.0.110
    pw = www
    db = example1
    import configparser
    conf = configparser.ConfigParser()
    conf.read('format.conf')
    print(conf.get('db1', 'con_str'))
    print(conf.get('db2', 'con_str'))
    mysql://aaa:ppp@localhost:3306/example
    mysql://root:www@192.168.0.110:3306/example1

      通过get方法,格式化con_str的参数是不同的

    建议52:用发布订阅模式实现松耦合

      发布订阅模式的优点是发布者与订阅者松散的耦合,双方不需要知道对方的存在。由于主题是被关注的,发布者和订阅者可以对系统拓扑毫无所知。无论对方是否存在,发送者和订阅者都可以继续正常操作。要实现这个模式,就需要有一个中间代理人,在实现中一般被称为Broker,它维护这发布者和订阅者的关系:订阅者把感兴趣的主题告诉它,而发布者的信息也通过它路由到各个订阅者处。

      假定你在编写一个非常牛X的程序库,姑且为它取名为 foo,里面有一个函数叫 bar,你就想啊,这么牛X的一个函数,肯定要写一下 log 啊,所以你就写了以下代码:

    def bar():  
        print 'Haha, Calling bar().'  
        do_sth()  

      你高高兴兴发了版本,大家都过得很好。过了几天,公司的另一个项目组听闻牛人您写了个库叫 foo,非常好用,就拿去用了。当天,快下班的时候,你被拖去救火,因为出 Bug 了呀。你查看了很久日志,都没有发现他们调用 bar() 的痕迹,一问,原来他们是用 logging 的,标准输出在做 Daemon 的时候被重定向到 /dev/null 去了……。

      好吧,你忍。但没法忍啊,你们原来的项目又不用 logging,你在程序库里引入 logging 谁来初始化它呢?就算你引入了 logging,你们项目获取 logger 可能是用 logging.getLogger('prjA'),另一个项目可能是用 logging.getLogger('prjB'),日后还有新项目呢,想到这个你就蛋疼了。忍痛割爱,把 print 语句给删除掉?你又怕日后出了问题你自己都找不到 Bug 那还不是自己加班自己苦……。

      这个时候,不妨让 python-message 来帮你手,轻松改一下 bar() 函数

    import message 
    LOG_MSG = ('log', 'foo') 
    def bar(): 
        messeage.pub(LOG_MSG, 'Haha, Calling bar().') 
        do_sth() 

      而在你的项目中,只需要在项目开始处加上这样的代码:

    import message 
    import foo 
    def handle_foo_log_msg(txt): 
        print txt 
    message.sub(foo.LOG_MSG, handle_foo_log_msg) 

      而很类似地,在另一个项目 prjA 里,你可以把 handle_foo_log_msg() 稍作修改:

    def handle_foo_log_msg(txt): 
        import logging 
        logging.debug(txt) 

      在另一个 prjB 里则可能是这样:

    import logging 
    logger = logging.getLogger("prjB") 
    def handle_foo_log_msg(txt): 
        logger.debug(txt) 

      通过sub('greet', hello, front=True),可以将后sub的函数先被调用

      订阅/发布模式是观察者模式的超集,它不关注消息是谁发布的,也不关心消息由谁处理。但有时我们也希望某个自己的类也能更方便的订阅/发布消息,也就是想退化为观察者模式,python-message同样提供支持。

    建议53:用状态模式美化代码

      所谓状态模式,就是当一个对象的内在状态改变是允许改变其行为,但这个对象看起来像是改变了其类,状态模式主要用于控制一个对象状态的条件表达式过于复杂的情况,其可把状态的判断逻辑转移到表示不同状态的一系列类中,进而把复杂的判断逻辑简化。

    建议55:__init__()不是构造方法

    class A(object):
        def __new__(cls, *args, **kwargs):
            print(cls)
            print(args)
            print(kwargs)
            print("------------------")
            isinstance = object.__new__(cls)
            print(isinstance)
            # return super(A, cls).__new__(cls)
    
        def __init__(self, a, b):
            print("init gets called")
            print("self is ", self)
            self.a, self.b = a, b
    
    
    a = A(1, 2)
    print(a.a)
    print(a.b)

      __new__()才是构造方法,__new__()方法一般需要返回类的对象,当返回类的对象是将会自动调用__init__()方法进行初始化,如果没有对象返回,则__init__()方法不会被调用。__init__()方法不需要显示返回,默认为None,否则会在运行时抛出TypeError。

      实例创建的时候使用__new__()方法,实例初始化的时候使用__init()方法。

      一般情况下不需要覆盖__new__()方法,但当子类继承自不可变类型,如str、int、unicode或者tuple的时候,往往需要覆盖该方法。

      当需要覆盖__new__()和__init__()方法的时候这两个方法的参数必须保持一致,如果不一致将导致异常。

     建议57:为什么需要self参数

    import math
    
    
    def len(point):
        print(point.right_angle_sideX, point.right_angle_sideY)
        return math.sqrt(point.right_angle_sideX ** 2 + point.right_angle_sideY ** 2)
    
    class RTriangle(object):
        def __init__(self, right_angle_sideX, right_angle_sideY):
            self.right_angle_sideX = right_angle_sideX
            self.right_angle_sideY = right_angle_sideY
    
    RTriangle.len = len
    rt = RTriangle(3, 4)
    print(rt.len())

    建议58:理解MRO与多继承

      在古典类中,MRO搜索采用简单的自左至右的深度优先方法,即按照多继承申明的顺序形式继承树结构,自顶向下采用深度优先搜索顺序,当找到所需要的属性或方法的时候就停止搜索;

      在新式类中,采用的是C3 MRO搜索方法,广度优先的方法

    建议60:区别__getattr__()和__getattribute__()方法

      __getattr__()和__getattribute__()方法都可以用做实例属性的获取和拦截,__getattr__()适用于未定义的属性,即该属性在实例中以及对应的类的基类以及祖先类中都不存在,而__getattribute__()对于所有的访问都有会调用该方法。__getattribute__()仅应用于新式类,

    class A(object):
        def __init__(self, name):
            self.name = name
    
    a = A("allin")
    print(a.name)
    print(a.test)

       当访问一个不存在的实例属性的时候就会抛出AttributeError异常,这个异常是由内部方法__getattribute__(self, name)抛出的,因为__getattribute__()会被无条件调用,也就是说只要涉及实例属性的访问就会调用该方法,它要么返回实际的值,要么抛出异常。我们在上面例子中添加__getattr__()方法:

    class A(object):
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, item):
            print("calling __getattr__: ", item)
    
    a = A("allin")
    print(a.name)
    print(a.test)

       这次程序没有抛出异常,而调用了__getattr__()方法,实际上__getattr__()方法仅如下情况才被调用:

        属性不在实例的__dict__中;

        属性不在基类以及祖先类的__dict__中;

        触发AttributeError异常时。

      需要特别注意的是两个方法同时被定义的时候,要么在__getattribute__()中显示调用,要么触发AttributeError异常,否则__getattr__()永远不会被调用,两个方法都是Object类中定义的默认方法,当用户需要覆盖这些方法时有以下几点注意事项:

        1)避免无穷递归:上面例子如果添加__getattribute__()就会无穷递归,因为属性的访问调用的是覆盖了的__getattribute__()方法,而该方法中self.__dict__[attr]又调用__getattribute__()

        2)访问未定义的属性

      例子:

    class A(object):
        _c = "test"
    
        def __init__(self):
            self.x = None
    
        @property
        def a(self):
            print("using property to access attribute")
            if self.x is None:
                print("return value")
                return "a"
            else:
                print("error occured")
                raise AttributeError
    
        @a.setter
        def a(self, value):
            self.x = value
    
        def __getattr__(self, item):
            print("using __getattr__ to access attribute")
            print("attribute name: ", item)
            return "b"
    
        def __getattribute__(self, item):
            print("using __getattribute__ to access attribute")
            return object.__getattribute__(self, item)
    
    
    a1 = A()
    print(a1.a)
    print("-" * 50)
    a1.a = 1
    print(a1.a)
    print("-" * 50)
    
    print(A._c)

       说明:第一部分访问a1.a时会先调用__getattribute__()方法,然后进入a方法,当判断self.x的时候又一次访问属性,又一次调用__getattribute__()方法,...

             第二部分打印 error occured 后会调用__getattr__()方法....

          第三部分是为了说明调用类属性不会调用__getattribute__()和_getattr__()方法。

    人生就是要不断折腾
  • 相关阅读:
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.3 日历
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.3 定时器
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.2 activation-group& dialect& date-effective
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.2 auto-focus
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.2 agenda-group
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.2 ruleflow-group&salience
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.2 lock-on-active
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.2 no-loop
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第4章 4.1 规则文件
    【java规则引擎】《Drools7.0.0.Final规则引擎教程》第3章 3.2 KIE API解析
  • 原文地址:https://www.cnblogs.com/xiangxiaolin/p/12861062.html
Copyright © 2011-2022 走看看