优雅地解决 Python 的日常问题

栏目: Python · 发布时间: 6年前

内容简介:什么是优雅的代码,今天大邓与你一起学习优雅的python代码。本文内容根据PyCon2018(克利夫兰)其实我们平常使用的列表、字符串等数据类型的一些方法就用到了莫属方法,比如这个字符串拼接操作,在字符串类的定义中使用了

什么是优雅的代码,今天大邓与你一起学习优雅的 python 代码。本文内容根据PyCon2018(克利夫兰) Nina Zakharenko - Elegant Solutions For Everyday Python Problems - PyCon 2018 大会演讲视频整理而来。

python魔术方法-双下划线

其实我们平常使用的列表、字符串等数据类型的一些方法就用到了莫属方法,比如

a = 'edf'
b = 'ggg'
print(a+b)

这个字符串拼接操作,在字符串类的定义中使用了 __add__   这个魔法。现在我们定义Money类来表示不同的货币,并能计算汇率。

class Money:
    #这里我们以美元作为计价单位1,方便理解
    current_rates = {'$':1,
                     '¥':6}
    
    def __init__(self, symbol, amount):
        self.symbol = symbol
        self.amount = amount    
    def __str__(self):
        #用来将实例化显示出来
        return f'{self.symbol}{self.amount}'
    
    def convert(self, other):
        #将rmb转化为美元计价
        new_amount = other.amount/self.current_rates[other.symbol]*self.current_rates[self.symbol]
        return Money(self.symbol, new_amount)
    
    
    dollar = Money('$', 5)rmb = Money('¥', 5)

print(dollar)
print(rmb)
print(rmb.convert(dollar))

运行结果

$5
¥5
¥5.0

现在我们想计算这个人持有的dollar和rmb一共值多少钱,这里就用到 加法__add__

class Money:
    current_rates = {'$':1,
                     '¥':6}
    
    def __init__(self, symbol, amount):
        self.symbol = symbol
        self.amount = amount    
    def __str__(self):
        return f'{self.symbol}{self.amount}'
    
    def convert(self, other):
        #汇率换算
        new_amount = other.amount/self.current_rates[other.symbol]*self.current_rates[self.symbol]
        return Money(self.symbol, new_amount)
    
    def __add__(self, other):
        #将两种不同的货币进行总价值计算
        new_amount = self.amount + self.convert(other).amount        return Money(self.symbol, new_amount)
        
    dollar = Money('$', 5)rmb = Money('¥', 5)

print(dollar)
print(rmb)
print(dollar+rmb)
print(rmb+dollar)

运行结果

$5
¥5
$5.833333333333333
¥35.0

此外还有 __getitem__ 、__len__   等更多的魔术方法,比如

class SquareShape:
    def __len__(self):
        #返回正方向的边数
        return 4
    my_square = SquareShape()

len(my_square)

运行结果

可迭代类

  • 为了创建可迭代的(iterable)数据类型,定义时需要用到 __iter__()

  • __iter__()   必须返回迭代器iterator

  • 为了让数据是迭代器iterator,必须使用 __next__() , 当迭代器中没有更多的元素可供迭代,此时raise   StopIteration ,iterator不再进行迭代。

比如我们在这里定义一个可迭代数据类型IterableServer

class IterableServer:
    services = [{'protocol':'ftp', 'port':21},
                {'protocol':'ssh', 'port':22},
                {'protocol':'http', 'port':80}]
    
    def __init__(self):
        #初始化服务器索引位置为第一个
        self.current_index = 0
        
    def __iter__(self):
        #没有此方法,IterableServer就不能for循环迭代
        return self    
    def __next__(self):
        while self.current_index < len(self.services):
            service = self.services[self.current_index]
            self.current_index+=1
            return service['protocol'], service['port']
        raise StopIteration     
    #这是咱们平常使用的for循环
servers = IterableServer()
print(servers)
for s in servers:
    print(s)

运行结果

<__main__.IterableServer object at 0x1092ece10>
('ftp', 21)
('ssh', 22)
('http', 80)

< main .IterableServer object at 0x1092a5898>说明我们是迭代器对象,可以使用for循环,这个有点像列表。每次for循环,我们都要iter自己本身。所以比较消耗内存空间。

现在我们将IterableServer中的 iter 重新定义,使用yield,让IterableServer变成生成器,每次循环只迭代当前位置的元素,而不是将本身全部迭代。

class IterableServer2:
    services = [{'protocol':'ftp', 'port':21},
                {'protocol':'ssh', 'port':22},
                {'protocol':'http', 'port':80}]
    
    def __init__(self):
        #初始化服务器索引位置为第一个
        self.current_index = 0
        
    def __iter__(self):
        for service in self.services:
            yield service            
    def __next__(self):
        while self.current_index < len(self.services):
            service = self.services[self.current_index]
            self.current_index+=1
            return service['protocol'], service['port']
        raise StopIteration
        #这是咱们平常使用的for循环
servers2 = IterableServer2()
print(servers2)
for s in servers2:
    print(s)

运行结果

<__main__.IterableServer2 object at 0x1092ecc88>
{'protocol': 'ftp', 'port': 21}
{'protocol': 'ssh', 'port': 22}
{'protocol': 'http', 'port': 80}

检验下运行速度(时间)

def s1():
    servers = IterableServer()
    for s in servers:
        s    def s2():
    servers2 = IterableServer2()
    for s in servers2:
        s%timeit s1()
1.06 µs ± 48.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit s2()
996 ns ± 39.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

从上面的运行时间看,IterableServer2比IterableServer快,大家可以以此来理解同样的数据,使用列表与生成器的速度是不同的。

getattr(object, name, default)

举例这是我们正常的方法调用

class Dog:
    sound = 'Bark'
    def speak(self):
        print(self.sound +'!',self.sound+'!')

my_dog = Dog()
my_dog.speak()

运行结果

Bark! Bark!

使用getattr可以让我们通过使用字符串去调用实例中的方法

speak_method = getattr(my_dog, 'speak')
speak_method()

运行结果

Bark! Bark!

现在可能觉得区别不大,好像没必要学getattr。但是假设定义的类中有很多种方法,在某种情况下我们需要输入一个命令的名字,并执行这个方法,就用到getattr

class Operations:
    def say_hi(self, name):
        print('hello, ', name)
    def say_bye(self, name):
        print('Goodbye, ', name)
    def default(self, arg):
        print('Operations不存在这个方法')

operations = Operations()
getattr(operations, 'say_hi', operations.default)('David')

运行结果

hello,  David
getattr(operations, 'say_hiiii', operations.default)('David')

运行结果

Operations不存在这个方法

装饰器

装饰器可以用来让我们的代码更简洁美观,我们看一个例子。比如我们要举行一个会议,只让授权的人参加。

class User:
    def __init__(self, name, is_authenticated=False):
        self.name = name
        self.is_authenticated = is_authenticated    
    def __str__(self):
        return '<User {}>'.format(self.name)
        


user1 = User('david')
user2 = User('smith', True)
user3 = User('sam', True)
users = [user1, user2, user3]
for u in users:
    if u.is_authenticated == True:
        print(u,'已授权,可以参加会议')

运行结果

<User smith> 已授权,可以参加会议
<User sam> 已授权,可以参加会议

但是涉及到检验某人是否有权限部分的代码不美观简洁,

def check(func):
    def wrapper(user):
        if not user.is_authenticated:
            raise Exception('抱歉,{}先生您未注册会议,无权进入会场'.format(user.name))
        return func(user)
    return wrapper

@check
def display_authenticated_user(user):
    print(user.name, '有权进入会场')
    

user1 = User('david')
user2 = User('smith', True)
user3 = User('sam', True)
users = [user2, user3, user1]
for u in users:
    display_authenticated_user(u)

运行结果

smith 有权进入会场
sam 有权进入会场



---------------------------------------------------------------------------

Exception                                 Traceback (most recent call last)

<ipython-input-106-c6d3c0348559> in <module>()
     17 
     18 for u in users:
---> 19     display_authenticated_user(u)


<ipython-input-106-c6d3c0348559> in wrapper(user)
      2     def wrapper(user):
      3         if not user.is_authenticated:
----> 4             raise Exception('抱歉,{}先生您未注册会议,无权进入会场'.format(user.name))
      5         return func(user)
      6     return wrapper


Exception: 抱歉,david先生您未注册会议,无权进入会场

精选文章

五分钟带你了解随机森林

聊天机器人:十行代码让你秒变撩妹达人

100G 文本分析语料资源(免费下载)   

15个最好的数据科学领域Python库

使用Pandas更好的做数据科学

抓取8w技能交换公告信息

视频讲解】Scrapy递归抓取简书用户信息

美团商家信息采集神器

大邓强力推荐-jupyter notebook使用小技巧

10分钟理解深度学习中的~卷积~

深度学习之 图解LSTM

PyTorch实战: 使用卷积神经网络对照片进行分类

Pytorch实战:使用RNN网络对姓名进行分类

数据清洗 常用正则表达式大全

PySimpleGUI: 开发自己第一个软件

深度特征合成:自动生成机器学习中的特征

Python 3.7中dataclass的终极指南(一)

Python 3.7中dataclass的终极指南(二)

[计算消费者的偏好]推荐系统与协同过滤、奇异值分解

机器学习: 识别图片中的数字

应用PCA降维加速模型训练

如何从文本中提取特征信息?

使用sklearn做自然语言处理-1

使用sklearn做自然语言处理-2

机器学习|八大步骤解决90%的NLP问题      

Python圈中的符号计算库-Sympy

Python中处理日期时间库的使用方法  

用chardect库解决网页乱码问题

优雅地解决 Python 的日常问题


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

大型网站系统与Java中间件开发实践

大型网站系统与Java中间件开发实践

曾宪杰 / 电子工业出版社 / 2014-4-24 / 65.00

本书围绕大型网站和支撑大型网站架构的 Java 中间件的实践展开介绍。从分布式系统的知识切入,让读者对分布式系统有基本的了解;然后介绍大型网站随着数据量、访问量增长而发生的架构变迁;接着讲述构建 Java 中间件的相关知识;之后的几章都是根据笔者的经验来介绍支撑大型网站架构的 Java 中间件系统的设计和实践。希望读者通过本书可以了解大型网站架构变迁过程中的较为通用的问题和解法,并了解构建支撑大型......一起来看看 《大型网站系统与Java中间件开发实践》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具