python学习笔记6.7-简化数据结构的初始化过程

我们每编写一个类的时候都需要编写一个初始化函数,那么如果编写的类当做数据结构来用,它们的初始化结构就是一样的,例如:

class Stock:
    def __init__(self,name,shares,price):
        self.name = name
        self.shares = shares
        self.price = price

class Point:
    def __init__(self,x,y):
        self.x = x
        self.y = y

class Circle:
    def __init__(self,radius):
        self.radius = radius

每一个类写一个数据结构这样比较麻烦,可以利用其它有效的方法去避免这种麻烦,加快编写代码的速度,例如,将初始化数据结构的步骤归纳到一个单独的init()函数中,并将其定义在公共的基类中:

class Structure:
    _fields=[]
    def __init__(self,*args):
        if len(args) != len(self._fields):
            raise TypeError('Expected {} arguements'.format(len(self._fields)))
        #set the arguments
        for name, value in zip(self._fields,args):
            setattr(self,name,value)

class Stock(Structure):
    _fields = ['name','shares','prices']
s = Stock('ACER',50,99)
print(s.name,s.shares,s.prices)

class Point(Structure):
    _fields = ['x','y']
p = Point(4,5)
print(p.x,p.y)

打印输出:
ACER 50 99
4 5

这样我们还可以继续创建各种基于Structure的类,方便快捷。究其本质而言,这种方法很好的利用了类的继承。 可以对上面的方法进行完善,对其添加对关键字参数的支持,这样表达更清晰,更方便编程,最好的选择就是对关键字参数做映射,这样它们就只能对应于定义在-fields中的属性名:

class Structure:
    _fields=[]
    def __init__(self,*args,**kwargs):
        if len(args) > len(self._fields):
            raise TypeError('Expected {} arguments'.format(len(self._fields)))
        #set the arguments
        for name, value in zip(self._fields,args):
            setattr(self,name,value)
        for name in self._fields[len(args):]:
            setattr(self,name,kwargs.pop(name))
        if kwargs:
            raise TypeError('Invided arguments : {}'.format(','.join(kwargs)))

class Stock(Structure):
    _fields = ['name','shares','prices']
s = Stock('ACER',50,prices=99)
print(s.name,s.shares,s.prices)

class Point(Structure):
    _fields = ['x','y']
p = Point(x=4,y=5)
print(p.x,p.y)

打印输出:
ACER 50 99
4 5

还可以利用关键字参数来给类添加额外的属性,而这些额外的属性是没有定义在_fields中的:

class Structure:
    _fields=[]
    def __init__(self,*args,**kwargs):
        if len(args) > len(self._fields):
            raise TypeError('Expected {} arguments'.format(len(self._fields)))
        #set the arguments
        for name, value in zip(self._fields,args):
            setattr(self,name,value)
        for name in self._fields[len(args):]:
            setattr(self, name, kwargs.pop(name))
        extra_args = kwargs.keys() - self._fields
        for name in extra_args:
            setattr(self,name,kwargs.pop(name))
        if kwargs:
            raise TypeError('Invided arguments : {}'.format(','.join(kwargs)))

class Stock(Structure):
    _fields = ['name','shares','prices']
s = Stock('ACER',50,prices = 99,date = '2012-2-2')
print(s.name,s.shares,s.prices,s.date)

class Point(Structure):
    _fields = ['x','y']
p = Point(4,5)
print(p.x,p.y)

从示例中可以发现:我们都是使用setattr()函数来将传递进来的属性参数添加到对应的属性上。与此不同的是,还可以选择直接访问示例字典的办法来添加属性。

class Structure:
    _fields=[]
    def __init__(self,*args,**kwargs):
        if len(args) > len(self._fields):
            raise TypeError('Expected {} arguments'.format(len(self._fields)))
        #set the arguments
        self.__dict__.update(zip(self._fields,args))

class Stock(Structure):
    _fields = ['name','shares','prices']
s = Stock('ACER',50,99)
print(s.name,s.shares,s.prices)

class Point(Structure):
    _fields = ['x','y']
p = Point(4,5)
print(p.x,p.y)

这么写看似高端简单,但是对于python程序来说是不严谨的。如果某个子类使用slot()方法或者@property(或者描述符)包装了某个特定的属性,直接访实例的字典就会产生崩溃。因而不建议使用这种写法。

尽管简化数据结构的几种方法都十分的实用,但是它的缺点就是会影响到IDE的文档和帮助,如果用户针对于某个特定的类寻求帮助,那么所需的参数就不会以正常的形式来表达。

print(help(Stock))
打印输出:
class Stock(Structure)
 |  Method resolution order:
 |      Stock
 |      Structure
 |      builtins.object
 |  
 |  Data and other attributes defined here:
 |  
 |  _fields = ['name', 'shares', 'prices']
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from Structure:
 |  
 |  __init__(self, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Structure:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

None

虽然可以通过在init()中强制签名来解决这个bug,但是不仅会增加程序员的工作量,而且运行速度也比较慢,所以不推荐。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端架构与工程

《JQuery技术内幕》读书笔记——自调用匿名函数剖析

Javascript语言中的自调用匿名函数格式如下: (function(){ //do somethings })(); 它还有另外两种等价写法如下: //等...

1828
来自专栏精讲JAVA

Git 内部原理之 Git 对象哈希

在上一篇文章中,将了数据对象、树对象和提交对象三种Git对象,每种对象会计算出一个hash值。那么,Git是如何计算出Git对象的hash值?本文的内容就是来解...

1162
来自专栏Java技术分享

Redis常用命令整理

doc 环境下使用命令:     keys 命令         ?    匹配一个字符         *    匹配任意个(包括0个)字符         ...

2017
来自专栏Java技术分享

Redis常用命令整理

        *    匹配任意个(包括0个)字符

2059
来自专栏白驹过隙

Redis - list类型操作

33812
来自专栏FreeBuf

PHP代码安全杂谈

虽然PHP是世界上最好的语言,但是也有一些因为弱类型语言的安全性问题出现。WordPress历史上就出现过由于PHP本身的缺陷而造成的一些安全性问题,如CVE-...

2126
来自专栏debugeeker的专栏

《coredump问题原理探究》Linux x86版4.2节函数的逆向之顺序结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

401
来自专栏python百例

118-ip地址与10进制数的转换

当我们ping数字2130706433时,从127.0.0.1返回结果。为什么是这样呢? IP地址是个32位的二进制数,表示成点分10进制,只是为了方便,如果...

803
来自专栏玄魂工作室

如何学python-第二课 基础输出/变量/变量类型

在python和其他主流的脚本语言里,有几样东西会在你在漫长的编程生涯里一直伴随着你——不外乎输出,变量,以及变量的类型。我们会从输出介绍起,然后是变量和变量的...

2707
来自专栏我是攻城师

ElasticSearch聚合查询小例子

2613

扫码关注云+社区