EFFECCTIVE PYTHON读书笔记(21~30)

21 用只能以关键字形式指定的参数来却表代码明晰

我们使用关键字来指定python的参数可以以指定参数的方式调用函数了

但是如果我们只想让使用者以指定参数的方式来调用函数的话该怎么做呢?python也有办法

def func(a,b,*,c,d):
    pass

看到这个函数的定义了不?这个参数里面有个奇怪的*,意思就是说*号后面的参数就必须使用指定参数的方式调用,而非指定参数的话,改函数只能支持2个,也就是*前面的两个

22 尽量用辅助类来维护程序的状态,而不要用字典和元祖

  • 不要使用包含其他字典的字典,也不要使用过长的元祖
  • 如果容器中包含简单而又不可变的数据,那边可以先使用namedtuple来表示,稍后有需要时再改为完整的类
  • 保存内部状态的字典如果变得复杂那么就应该将这些字典拆解成新的辅助类

这里有个namedtuple这个东东是以前没用过的,我用了用试下


import collections
# 这个有点像是定义了一个新类型
# 类型名字为NewTupleName
# 他有三个成员常量(不可修改)分别为name1,name2,name3
NewTuple = collections.namedtuple('NewTupleName', 'name1, name2, name3')
# 这里展示下如何创建实例
item = NewTuple(name1="aaa", name2="bbb", name3="ccc") #这里也可以这么写 item = NewTuple("aaa","bbb","ccc",)
# 下面展示下如何使用
print(item) # 输出   NewTupleName(name1='aaa', name2='bbb', name3='ccc')
print(item.name1) # 输出 aaa

23 简单的接口应该接受函数,而不是类的实例

  • 对于连接各种python组件的简单接口来说,通常应当直接传入函数,而不是预定义某个类,再传入该类的实例
  • 在python中函数和方法都可以想类那样引用,因此,他们与其他类型的对象一样,也能放到表达式里
  • 通过名为__call__的特殊方法,可以是类能想普通函数一样使用
  • 如果要用函数来保存状态那么就应当定义新的类,并令其实现__call__方法,而不要定义带状态的闭包

上面提到一个__call__
这里简单演示下

#定义一个带__call__的类
class Demo:
    def __call__(self, param1):
        print(param1)
#实例化Demo类
demo = Demo()
#像使用函数一样使用Demo的实例
demo(5566)

24 以@classmethod形式的多态去通用的构建对象

  • 在python程序中,每个类只能有一个构造器也就是__init__方法
  • 通过@classmethod机智,可以用一种与构造器相仿的方式来构造类的对象
  • 通过类方法多态机智,我们能够更加通用的方式来构建和拼接具体子类

上面提到2个东东一个是__init__方法,我觉得这个用的不较多,基本每个类都有就不说了,
关于@classmethod的话…

#定义classmethod的函数
class Demo:
    data1=999
    #定义cls表示类,而通过类函数是可以直接访问类的成员你变量data1的,但是不可访问实例变量
    @classmethod
    def method(cls):
        print(cls)
        print(cls.data1)
Demo.method()

25 用super初始化父类

对于python来说,必须使用super来显式的初始化父类。然而这里有个各种语言都有的难题,就是多重继承,或者钻石型继承的问题。
但对于python来说看个例子
继承关系如下

类ClassA
类ClassB
类ClassC
类ClassD

正常的代码如下

class ClassA(object):
    def __init__(self):
        print("ClassA")
class ClassB(ClassA):
    def __init__(self):
        print("ClassB")
        ClassA.__init__(self)
class ClassC(ClassA):
    def __init__(self):
        print("ClassC")
        ClassA.__init__(self)
class ClassD(ClassB, ClassC):
    def __init__(self):
        print("ClassD")
        ClassB.__init__(self)
        ClassC.__init__(self)
d = ClassD()
#输出为
"""
ClassD
ClassB
ClassA
ClassC
ClassA
"""

这里发现ClassA的初始化函数被调用了2次???
这貌似和我们想的有点一样,又有点不一样。。。
那如果我们只想调用一次该如何呢?是ClassB不初始化父类,还是ClassC不初始化父类呢?
好像都不合适,于是
用super实现的方式如下所示!


class ClassA(object):
    def __init__(self):
        print("ClassA")
class ClassB(ClassA):
    def __init__(self):
        print("ClassB")
        super(ClassB, self).__init__()
class ClassC(ClassA):
    def __init__(self):
        print("ClassC")
        super(ClassC, self).__init__()
class ClassD(ClassB, ClassC):
    def __init__(self):
        print("ClassD")
        super(ClassD, self).__init__()
print(ClassD.mro())
d = ClassD()
#输出为
"""
ClassD
ClassB
ClassC
ClassA
"""

HOHO,这个结果如何?是不是和我们想的差不多啦!
有学到了点新东西。。。
最后,总的来说,还是少用多重继承的好

26 只在使用Mix-in组件制作工具时进行多重集成

简单的说,就是别用多重继承!!!!除非mix-in

27 多用public属性,少用private属性

python中其实并没有严格的public,protected, private,只是用一种命名规范来表示
单下划线开头的表示为protected,双下划线开头的表示为private。

  • python编译器无法严格保证private字段的私密性
  • 不要盲目的设置private,而是应该从一开始就做好规划,并允许子类更多访问超类内部的api
  • 应该多用protected属性,并在文档中把这些字段的合理用法告诉子类的开发者,不要试图用private属性来限制子类访问这些字段
  • 只有当子类不受自己控制时,才可以考虑使用private属性来避免名称冲突。

28 继承collection.abc以实现自定义的容器类型

  • 如果要定制的子类比较简单,那就可以直接从python的容易继承即可
  • 想要实现自定义的容器类型,需要编写大量大量大量大量的特殊方法,很难细细描述
  • 编写自定义容器的时候,可以从collections.abc模块的抽象类中继承,那些积累能够确定子类中具有适当的接口及行为。

29 用纯属性取代get和set方法

  • 编写新类时,应该用简单的public属性来定义接口,而不要手工实现set和get方法
  • 如果访问某个对象属性时需要表现出特殊的行为,那就用@property来定义这种行为
  • @property方法应当遵循最小惊讶原则,而不应产生奇怪的副作用
  • @property方法需要执行迅速一些,复杂或者缓慢的工作应当放到普通的方法里

普通的语言,对于修改私有变量这种事情,一般喜欢使用set和get方法,但是python不建议这种方式。
由于习惯于set和get,这种方法没咋用过,我下面用用展示下
下面示例下

class ClassA:
    def __init__(self):
        self._param1= 10
    #@property表示该函数的使用类似于一个成员变量一般,也就是属性
    @property
    def param1(self):
        print("get param1")
        return self._param1
    #@param1.setter表示param1作为一个属性被设置时调用该函数
    @param1.setter
    def param1(self, new_value):
        print("set param1 to",new_value)
        self._param1 = new_value
a = ClassA()
a.param1 = 12
print(a.param1)
#输出粗为
"""
set param1 to 12
get param1
12
"""

考虑用@property来代替属性重构

  • @property可以为现有的实例添加新的功能
  • 可以用@property来逐步完善数据模型
  • 如果用@property太过平凡,那就应该考虑彻底重构该类并修改相关调用代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值