Skip to content

Latest commit

 

History

History
441 lines (312 loc) · 8.81 KB

builtins4.md

File metadata and controls

441 lines (312 loc) · 8.81 KB

Python内建函数(四)

本篇文章将继续介绍Python的内建函数。

help()

在交互模式下,help()能够进入交互式的帮助文档中。我们可以输入内置模块、函数、关键字等信息来获得其对应的帮助文档:

>>> help()

Welcome to Python 3.7's help utility!
...
help>

这里我们直接输入任何内建内容即可获得其对应的使用方式:

help> builtins
Help on built-in module builtins:
    
NAME
    builtins - Built-in functions, exceptions, and other objects.
...

help> for

The "for" statement
*******************
...

我们还可以直接在交互界面使用help(object)来获得一个对象的文档:

>>> import logging
>>> help(logging)
Help on package logging:

NAME
    logging
...

对于自定义的函数或类,help能够给出他们的签名以及文档字符串的信息:

def func(x, y=1):
    """ This function is used to calculate x + y """
    return x + y

help(func)

# Help on function func in module __main__:

# func(x, y=1)
#     This function is used to calculate x + y

hex()

返回一个整数对应的16进制字符串,全小写,带有0x前缀:

hex(15)
# '0xf'

eval(hex(15))
# 15

如果希望去掉前缀,需要利用字符串格式化:

print(f"{15:X}")
"F"

id()

返回一个对象的唯一的身份值。这一身份值在对象生命周期内是不会变的。我们可以通过id值来判断两个标识符是否指向了同一个对象:

a = [1, 2, 3]
print(id(a))
# 139635118620616

b = a
print(id(b))
# 139635118620616

c = a[:]
print(id(c))
# 139635118170184

可以看到,b = a实际上是不同的标识符引用了相同的对象,而c是重新复制了新的列表。

input

获得标准输入的数据:

a = input("Please input your username: ")
print(a)

# Please input your username: *input* Python
# Python

我们还可以利用sys.stdin获得标准输入。sys.stdin是类文件对象,因此,需要用readline等方法进行读取:

import sys

ipt = sys.stdin.readline()
print(ipt)

for ind, l in enumerate(sys.stdin):
    print(l)
    exec(l)
    if ind == 3:
        break
        
#*input* a = 1
# a = 1
# *input* b = 2
# b = 2
# *input* c = 3
# c = 3
# *input* print(a + b - c)
# print(a + b - c)
# 0

需要注意的是,直接执行外部的输入有可能导致恶意程序被执行,例如:

exec(input("Please input your Python statement:"))
# __import__("os").system("reboot")

系统将被重启。

实际上,Python中还存在另一种获取输入的方式,即标准库模块fileinput。它可以迭代读取多个文件中的每一行内容。当我们不提供参数时,它将默认读取标准输入的每一行,即input的功能:

# a.txt
This is file a

# b.txt
File b

# main.py
import fileinput

for line in fileinput.input():
    print(line)
    
# python main.py a.txt b.txt
# This is file a
#
# File b
#

# python main.py
# *input* a = 1
# a = 1
# 
# *input* b = 2
# b = 2
#

int

将一个对象转化为整数形式。

int(0.1)
0

int(1.5)
1

int(-2.3)
-2

int()
0

int也可以将多进制字符串转换为对应的10进制数值:

int('ff', base=16)
255

int('zz', base=36)
1295

int('0o77', 8)
63

对于自定义类型的对象,int会依次调用其3个特殊方法来得到一个整数,即__int____index____trunc__Python 3.8以上版本)。

import math

class Test:
    def __init__(self, value):
        self.val = value

    def __int__(self):
        print("__int__ is called")
        return math.floor(self.val)

    def __index__(self):
        print("__index__ is called")
        return math.ceil(self.val)

    def __trunc__(self):
        print("__trunc__ is called")
        return math.trunc(self.val)

    def __floor__(self):
        print("__floor__ is called")
        return math.floor(self.val)

    def __ceil__(self):
        print("__ceil__ is called")
        return math.ceil(self.val)

    def __round__(self):
        print("__round__ is called")
        return round(self.val)


a = Test(1.5)

print(int(a))
# int is called
# 1

del Test.__int__

print(int(a))
# index is called
# 2

del Test.__index__

print(int(a))
# trunc is called
# 1

del Test.__trunc__

print(int(a))
# TypeError: int() argument must be a string, a bytes-like object or a number, not 'Test'

为什么会存在__index____trunc__两个特殊方法呢?首先,__trunc__是用于实现math模块中的trunc()函数,它用于截断一个实数获得其整数部分

import math

math.trunc(1.1) # 1
math.trunc(-2.3) # 2

当实数大于0时,trunc等于floor;当实数小于0时,trunc等于ceil

math.floor(1.1) == math.trunc(1.1)
True

math.ceil(-2.3) == math.trunc(-2.3)
True

对于__index__,它是专为列表索引而设置的特殊方法。当一个自定义对象被用于列表索引时,Python将调用__index__方法来获得一个整数索引值。此时,即使存在__int__特殊方法,也会直接使用__index__

class Test:
    def __init__(self, value):
        self.val = value
        
    def __int__(self):
        print("__int__ is called")
        return int(self.val + 1)
    
    def __index__(self):
        print("__index__ is called")
        return int(self.val)
    

t = Test(1.5)
a = [1, 2, 3, 4]

print(a[t])
# __index__ is called
# 2

为索引单独设计一个__index__是因为__int__是更通用的强制类型转换工具。索引本身需要的是真正的整数,如果允许__int__作为索引值,那么将出现这样奇怪的写法:a[2.1:2.8],因为float实现了__int__

isinstance(obj, cls)

如果对象obj是类cls(可以是元组)的实例,返回True,否则返回False

print(isinstance(1, (int, float)))
True

issubclass(scls, cls)

如果类scls是类cls(可以是元组)的子类,返回True,否则返回False

import collections.abc as abc
print(issubclass(list, abc.Iterable))
True

关于isinstanceissubclass的话题,请看这里。

iter

返回一个迭代器对象。(什么是迭代器对象?戳这里)

对于iter(x)调用形式,Python会调用x的特殊方法__iter__,该方法应当返回一个迭代器。通常情况下,__iter__可以直接返回self,当然,对象还应当实现__next__方法来产生一系列迭代值:

import random

class Iterator:
    def __init__(self, total_len):
        self.total_len = total_len
        self.lst = [random.random() for _ in range(self.total_len)]
        self.ind = -1
        
    def __iter__(self):
        print("__iter__ is called")
        return self
    
    def __next__(self):
        if self.ind < self.total_len - 1:
            self.ind += 1
            return self.lst[self.ind]
        else:
            raise StopIteration
            
it = Iterator(5)
itor = iter(it)
print(next(itor))
print(next(itor))

# __iter__ is called
# 0.3351207121754606
# 0.15403614803925425

当然,我们可以直接在__iter__中实现一个生成器(生成器就是一种迭代器):

import random

class Iterator:
    def __init__(self, total_len):
        self.total_len = total_len
        self.lst = [random.random() for _ in range(self.total_len)]
        self.ind = -1
        
    def __iter__(self):
        print("__iter__ is called")
        while self.ind < self.total_len - 1:
            self.ind += 1
            yield self.lst[self.ind]
            
it = Iterator(5)
itor = iter(it)
print(next(itor))
print(next(itor))

# __iter__ is called
# 0.30371754260109685
# 0.621951649704986

from collections.abc import Iterator, Generator
print(isinstance(itor, Iterator))
True

print(isinstance(itor, Generator))
True

print(issubclass(Generator, Iterator))
True

有趣的是,iter还可以接收第二个参数setinel,这时iter的第一个参数必须是无参数的可调用对象,然后,iter会调用该对象的__next__特殊方法,并将自动检查返回值是否与setinel一致,如果一致,则产生一个StopIteration表示迭代结束。

import string
import random

class LetterGen:
    def __call__(self):
        print("__call__ is called")
		return random.choice(string.ascii_lowercase[:5])
        
        
for t in iter(LetterGen(), "a"):
    print(t)
    
# __call__ is called
# d
# __call__ is called
# e
# __call__ is called

我们可以用lambda表达式来实现简易的无参数可调用对象:

# a.txt
This is a test file.

# main.py
with open("a.txt", 'r') as f:
	for ch in iter(lambda: f.read(1), '\n'):
		print(ch, end='')
        
# This is a test file.