[Python] 07 - Statements --> Functions
故事背景
阶级关系
1. Programs are composed of modules.
2. Modules contain statements.
3. Statements contain expressions.
4. Expressions create and process objects.
教学大纲
Statements
大纲纵览
Python 3.X’s statements
- Python 3.X reserved words.
- 一些简单的示范例子,page 372/1594
a, b = 'good', 'bad' log.write("spam, ham") print('Then Killer', joke) if "python" in text:
print(text) for x in mylist:
print(x) while X > Y:
print('hello') while True:
pass while True:
if exittest(): break while True:
if skiptest(): continue def f(a, b, c=1, *d):
print(a+b+c+d[0]) # 其中一个*号代表list或者tuple,**代表map或者dict
def f(a, b, c=1, *d):
return(a+b+c+d[0]) def gen(n):
for i in n:
yield i*2 x = 'old'
def function():
global x, y:
x = 'new' # https://stackoverflow.com/questions/1261875/python-nonlocal-statement
def outer():
x = 'old'
def function():
nonlocal x:
x = 'new' import sys from sys import stdin class Subclass(Superclass):
staticData = []
def method(self):
pass try:
action()
except:
print('action error') # 需进一步理解
raise EndSearch(location) assert X > Y, 'X too small' with open('data') as myfile:
process(myfile) del data[k]
del data[i:j]
del obj.attr
del variable
一些值得注意的用法.
# 其中一个*号代表list或者tuple,**代表map或者dict
def f(a, b, c=1, *d):
return(a+b+c+d[0]) def gen(n):
for i in n:
yield i*2 x = 'old'
def function():
global x, y:
x = 'new' #https://stackoverflow.com/questions/1261875/python-nonlocal-statement
def outer():
x = 'old'
def function():
nonlocal x:
x = 'new' del data[k]
del data[i:j]
del obj.attr
del variable
- Closure 和 Non-local 的迷惑
大函数返回小函数,小函数仅能"只读"大函数的局部变量。
如果“可写”,则需要Non-local。
变量初始化
Level 1 - 弱初始化
Level 2 - 配对赋值
>>> A, B = nudge, wink # Tuple assignment >> [C, D] = [nudge, wink] # List assignment
# 于是,变量数值对调运算时 会简单一些
>>> nudge, wink = wink, nudge # Tuples: swaps values >>> [a, b, c] = (1, 2, 3) # Assign tuple of values to list of names >>> (a, b, c) = "ABC" # Assign string of characters to tuple >>> a, b, c = string[0], string[1], string[2:] # Index and slice
高级一点的,括号匹配。
for (a, b, c) in [(1, 2, 3), (4, 5, 6)]: ... # Simple tuple assignment
for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]: ... # Nested tuple assignment
Level 3 - rest赋值
方便个别变量赋值
不是指针,而是rest.
>>> a, *b, c = 'spam'
>>> a, b, c
('s', ['p', 'a'], 'm')
如果没有内容,则是认定为空列表 [ ].
# 末尾是否还有东西
>>> a, b, c, *d = seq
>>> print(a, b, c, d)
1 2 3 [4] >>> a, b, c, d, *e = seq
>>> print(a, b, c, d, e)
1 2 3 4 [] # 中间是否还有东西
>>> a, b, *e, c, d = seq
>>> print(a, b, c, d, e)
1 2 3 4 []
方便提取个别变量
循环遍历时貌似有一点点好处,such as "head var in a list".
>>> L = [1, 2, 3, 4]
>>> while L:
... front, *L = L # Get first, rest without slicing
... front, L = L[0], L[1:] # 上一行的另一种写法:小变化,效果一样
... print(front, L)
...
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
Level 4 - 用 “第一个非空值” 赋值
节省条件判断的变量赋值。
X = A or B or C or None
# assigns X to the first nonempty (that is, true) object among A, B, and C, or to None if all of them are empty. X = A or default
Expressions
Scope
全局变量 - global
各有各的作用域。
X = 99 # Global (module) scope X
def func():
X = 88 # Local (function) scope X: a different variable
细节繁琐,但大体是继承了C/C++的那一套东西。
• The enclosing module is a global scope. Each module is a global scope—that
is, a namespace in which variables created (assigned) at the top level of the module
file live. Global variables become attributes of a module object to the outside world
after imports but can also be used as simple variables within the module file itself.
• The global scope spans a single file only. Don’t be fooled by the word “global”
here—names at the top level of a file are global to code within that single file only.
There is really no notion of a single, all-encompassing global file-based scope in
Python. Instead, names are partitioned into modules, and you must always import
a module explicitly if you want to be able to use the names its file defines. When
you hear “global” in Python, think “module.”
• Assigned names are local unless declared global or nonlocal. By default, all
the names assigned inside a function definition are put in the local scope (the
namespace associated with the function call). If you need to assign a name that
lives at the top level of the module enclosing the function, you can do so by
declaring it in a global statement inside the function. If you need to assign a name
that lives in an enclosing def, as of Python 3.X you can do so by declaring it in a
nonlocal statement.
• All other names are enclosing function locals, globals, or built-ins. Names
not assigned a value in the function definition are assumed to be enclosing scope
locals, defined in a physically surrounding def statement; globals that live in the
enclosing module’s namespace; or built-ins in the predefined built-ins module
Python provides.
• Each call to a function creates a new local scope. Every time you call a function,
you create a new local scope—that is, a namespace in which the names created
inside that function will usually live. You can think of each def statement (and
lambda expression) as defining a new local scope, but the local scope actually
corresponds to a function call. Because Python allows functions to call themselves to
loop—an advanced technique known as recursion and noted briefly in Chapter 9
when we explored comparisons—each active call receives its own copy of the
function’s local variables. Recursion is useful in functions we write as well, to process structures whose shapes can’t be predicted ahead of time; we’ll explore it more
fully in Chapter 19.
Rules
"内部"使用"外层"的变量
X = 88 # Global X
def func():
global X
X = 99 # Global X: outside def
func()
print(X) # Prints 99
使用"其他文件"的变量
目的:Program Design: Minimize Cross-File Changes
# first.py
X = 99 # This code doesn't know about second.py
通过import使用其他文件(模块)的内容.
# second.py
import first
print(first.X) # OK: references a name in another file
first.X = 88 # But changing it can be too subtle and implicit
虽然是不同文件,加载到内存中就都一样了;通过函数改变总比直接改变要好一些。
# first.py
X = 99
def setX(new): # Accessor make external changes explit
global X # And can manage access in a single place
X = new
# second.py
import first
first.setX(88) # Call the function instead of changing directly
闭包 - factory methods
返回"一个产品"
也就是返回一个具有自己id的一类产品。【工程方法】
def maker(N):
def action(X): # Make and return action
return X + N # action retains N from enclosing scope
return action add_5 = maker(5) # 先设置"大函数"的参数
add_5(2) # 再设置"小函数"的参数
去掉"灵活"的参数,写成内部的固定的形式.
def maker():
n = 4
def action(x):
return x + n
return action f = maker()
f(2)
返回"一系列产品"
进一步,返回内部 id 不一样的一系列产品。【lambda指针数组】
def makeActions():
acts = []
for i in range(5): # Use defaults instead
acts.append(lambda x, i=i: i ** x) # Remember current i
return acts >>> acts = makeActions()
>>> acts[0](2) # 0 ** 2
0
>>> acts[1](2) # 1 ** 2
1
>>> acts[2](2) # 2 ** 2
4
>>> acts[4](2) # 4 ** 2
16
改变 “外层变量” - Non-local
Ref: python中global 和 nonlocal 的作用域
nonlocal 关键字 用来在函数或其他作用域中使用外层 (非全局) 变量。
由此,一系列产品间"产生了联系".
def make_counter():
count = 0
def counter():
nonlocal count # 表明想使用了外层变量!
count += 1
return count
return counter def make_counter_test():
mc = make_counter()
print(mc()) # 每调用一次,函数内部变量 count改变一次
print(mc())
print(mc()) make_counter_test()
输出:
1
2
3
Argument
已传进来
参数类型检查
对应type()参看变量类型.
数据类型检查可以用内置函数isinstance()
实现。
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
强复制拷贝
def changer(a, b):
b = b[:] # 拷贝技巧 a = 2
b[0] = 'spam' # Changes our list copy only
默认是引用,那么就是自己内部 copy 就好了。
在传递中
Arbitrary Arguments:星号传参(基础)
Table 18-1. Function argument-matching forms func(value) Caller Normal argument: matched by position
func(name=value) Caller Keyword argument: matched by name
func(*iterable) Caller Pass all objects in iterable as individual positional arguments
func(**dict) Caller Pass all key/value pairs in dict as individual keyword arguments def func(name) Function Normal argument: matches any passed value by position or name
def func(name=value) Function Default argument value, if not passed in the call
def func(*name) Function Matches and collects remaining positional arguments in a tuple
def func(**name) Function Matches and collects remaining keyword arguments in a dictionary
def func(*other, name) Function Arguments that must be passed by keyword only in calls (3.X)
def func(*, name=value) Function Arguments that must be passed by keyword only in calls (3.X)
The *name form collects any extra unmatched positional arguments in a tuple, and the **name form collects extra keyword arguments in a dictionary.
In Python 3.X, any normal or defaulted argument names following a *name or a bare * are keyword-only arguments and must be passed by keyword in calls.
- 一颗星,list
>>> def f(*args): print(args)
>>> f(1,2,3)
(1, 2, 3)
- 两颗星,dict
>>> def f(**args): print(args)
>>> f()
{}
>>> f(a=1, b=2)
{'a': 1, 'b': 2}
- 混合星
>>> def f(a, *pargs, **kargs): print(a, pargs, kargs)
>>> f(1, 2, 3, x=1, y=2)
1 (2, 3) {'y': 2, 'x': 1}
触发编译器自动判断(高阶)
加星号,让编译器去判断,*pargs,就可以在乱序的情况下自动调整。
def func(a, b, c, d):
print(a, b, c, d) >>> func(*(1, 2), **{'d': 4, 'c': 3}) # Same as func(1, 2, d=4, c=3)
1 2 3 4
>>> func(1, *(2, 3), **{'d': 4}) # Same as func(1, 2, 3, d=4)
1 2 3 4
>>> func(1, c=3, *(2,), **{'d': 4}) # Same as func(1, 2, c=3, d=4) <--值得注意下!
1 2 3 4
>>> func(1, *(2, 3), d=4) # Same as func(1, 2, 3, d=4)
1 2 3 4
>>> func(1, *(2,), c=3, **{'d':4}) # Same as func(1, 2, c=3, d=4)
1 2 3 4
Python 3.X Keyword-Only Arguments (难点),具体参见:591/1594
- 一颗星
必须让编译器知道*b代表的参数组的长度。也就是*b后的参数必须有明确赋值。
def kwonly(a, *b, c):
print(a, b, c)
>>> kwonly(1, 2, c=3)
1 (2,) 3
>>> kwonly(a=1, c=3)
1 () 3
>>> kwonly(1, 2, 3)
TypeError: kwonly() missing 1 required keyword-only argument: 'c'
也可以使用默认参数,默认参数是个好东西。
def kwonly(a, *b='spam', c='ham'):
print(a, b, c)
>>> kwonly(1)
1 spam ham
>>> kwonly(1, c=3)
1 spam 3
>>> kwonly(a=1)
1 spam ham
>>> kwonly(c=3, b=2, a=1)
1 2 3
>>> kwonly(1, 2) # 搞不清楚,2是b的,还是c的
TypeError: kwonly() takes 1 positional argument but 2 were given
- 两颗星
>>> def kwonly(a, **pargs, b, c):
SyntaxError: invalid syntax
>>> def kwonly(a, **, b, c):
SyntaxError: invalid syntax
>>> def f(a, *b, **d, c=6): print(a, b, c, d) # Keyword-only before **!
SyntaxError: invalid syntax
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # Collect args in header >>> f(1, 2, 3, x=4, y=5) # Default used
1 (2, 3) 6 {'y': 5, 'x': 4}
>>> f(1, 2, 3, x=4, y=5, c=7) # Override default
1 (2, 3) 7 {'y': 5, 'x': 4}
混合使用时,
>>> def f(a, c=6, *b, **d): print(a, b, c, d) # c is not keyword-only here!
>>> f(1, 2, 3, x=4)
1 (3,) 2 {'x': 4}
注意事项,
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # KW-only between * and ** # 直接告诉了编译器星号对应的参数(推荐)
>>> f(1, *(2, 3), **dict(x=4, y=5)) # Unpack args at call
1 (2, 3) 6 {'y': 5, 'x': 4} # 不能放在最后
>>> f(1, *(2, 3), **dict(x=4, y=5), c=7) # Keywords before **args!
SyntaxError: invalid syntax
>>> f(1, *(2, 3), c=7, **dict(x=4, y=5)) # Override default
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1, c=7, *(2, 3), **dict(x=4, y=5)) # After or before *
1 (2, 3) 7 {'y': 5, 'x': 4}
>>> f(1, *(2, 3), **dict(x=4, y=5, c=7)) # Keyword-only in **
1 (2, 3) 7 {'y': 5, 'x': 4}
“函数指针”参数
- 求极值
有点类似于模板,或者是函数指针之类。
def minmax(test, *args):
res = args[0]
for arg in args[1:]:
if test(arg, res):
res = arg
return res
def lessthan(x, y): return x < y # See also: lambda, eval
def grtrthan(x, y): return x > y
print(minmax(lessthan, 4, 2, 1, 5, 6, 3)) # Self-test code
print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))
如此,设计minmax这类的函数就省事些。将定义规则和遍历操作 ”相分离“。
Decorator
增强“无参”的原函数
Ref: Python 函数装饰器
Ref: 廖雪峰讲装饰器
Ref: 这是我见过最全面的Python装饰器详解!没有学不会这种说法!
Stage 1,使用函数指针,只能如下写法.
from functools import wraps
import time
# 这里写一个装饰器 fn_timer
def fn_timer(function):
@wraps(function)
def function_timer(*args, **kwargs):
#----------------------------------
start = time.time()
result = function(*args, **kwargs)
end = time.time()
print ("Total time: %s seconds" % (str(end-start)))
#----------------------------------
return result return function_timer
# @fn_timer
def print_hello():
print("hello") print_hello = fn_timer(print_hello) # 多写这么一句
print_hello()
Stage 2,为了少写这么一句,就使用@fn_timer。并且无需修改原代码。
from functools import wraps
import time # 这里写一个装饰器 fn_timer
def fn_timer(function):
@wraps(function)
def function_timer(*args, **kwargs):
#----------------------------------
start = time.time()
result = function(*args, **kwargs)
end = time.time()
print ("Total time: %s seconds" % (str(end-start)))
#----------------------------------
return result return function_timer @fn_timer
def print_hello():
print("hello") print_hello()
@wrapper 加不加?
决定了原函数是“原函数”?还是已经变成了“增强后的函数”。
也就是说,通过wraps告诉wrapper函数:“要彻底封装,不让使用者知道,还是留一点线索给他”。
from functools import wraps def test(func):
def wrapper(*args,**kwargs):
'''我是说明1'''
print('args: ', args)
print('kwargs: ',kwargs) args = (11,22,33)
kwargs['name'] = 'Test_C' return func(*args,**kwargs) return wrapper @test
def fun_test(*args,**kwargs):
'''我是说明2'''
print('我是一个函数')
print('---> ',args,kwargs) fun_test(1,2,3,4,a=123,b=456,c=789)
print('*'*20)
print(fun_test.__name__)
print(fun_test.__doc__)
- 应用场景
装饰器装饰“有参”函数
- 提出问题
上面的例子没有参数,如果有参数会如何?
因为只会去写 “一个装饰器”,所以 “参数个数不定” 也需要考虑进去。
- 解决问题
利用 *args, **kwargs即可。
装饰的函数,如果有返回值,则一致;如果没有返回值,则返回 None。
# 万能装饰器,修饰各种参数
def celebrator(func):
def inner(*args, **kwargs):
print('我是新功能')
ret = func(*args, **kwargs)
return inner @celebrator
def myprint(a):
print(a) myprint('python小白联盟')
装饰器本身带“参数”
不带参数,需要写两个类似的装饰器函数。
def printequal(func):
def inner():
print('=====')
func()
return inner def printstar(func):
def inner():
print('*****')
func()
return inner @printequal
@printstar
def myprint():
print('python jiu cai.') myprint()
带参数后,可将这两个函数合并。
def get_celebrator(char):
def print_style(func):
def inner():
print(char*5)
func()
return inner
return print_style
@get_celebrator('(')
@get_celebrator(')')
def myprint():
print('python jiu cai.') myprint()
Exception
系统常见异常
Ref: The definitive guide to Python exceptions
更多内容详见:Python标准异常列表
except OSError
except IOError
except ValueError
except ZeroDivisionError
except NameError
- isdigit 方法判断
“输入”的内容不好预测,也是“异常多发地”,故在此用来举例。
while True:
reply = input('Enter text:') if reply == 'stop':
break
elif not reply.isdigit(): # 如果不是数字
print('Bad!' * 8)
else: # 如果是数字
num = int(reply)
if num < 20:
print('low')
else:
print(num ** 2) print('Bye')
- except 捕获异常
处理策略不同,分开处理。
import sys try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.") except (RuntimeError, TypeError, NameError) # 如果处理策略一致,统一起来写
pass
except:
print("Unexpected error:", sys.exc_info()[0])
raise
else: # 在try子句没有发生任何异常的时候执行
pass
[意义何在?]
使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到的、而except又没有捕获的异常。
ref: Python中try…except…else…结构中else的作用?
既然执行到了else,就说明“没有异常发生”。如此,代码更清晰更具有表现力。
- finally 关键字
保证了with as清理文件流的机制.
>>>def divide(x, y):
try:
result = x / y
except ZeroDivisionError:
print("division by zero!")
else:
print("result is", result)
finally:
print("executing finally clause") >>> divide(2, 1)
result is 2.0
executing finally clause >>> divide(2, 0)
division by zero!
executing finally clause >>> divide("", "")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
- 捕获try中的 “子函数异常”
>>>defthis_fails():
x = 1/0 # 子函数中抛出异常 >>> try:
this_fails()
except ZeroDivisionError as err:
print('Handling run-time error:', err) Handling run-time error: int division or modulo by zero
Why? 自己不处理,比如权限不够.交给外层函数的异常处理部分.
如下,这是简单做个warning。re-raise后,将异常传递给外层函数的 "异常处理部分"。
logger = logging.getLogger(__name__) try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
用户自定义异常
- 抛出异常
使用 raise 语句抛出一个指定的异常。
>>>try:
raise NameError('HiThere')
except NameErrorprint('An exception flew by!', NameError.value)
raise An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
- 自定义 de "异常类"
"异常类" 继承自 Exception 类,可以直接继承,或者间接继承。
>>>class MyError(Exception):
def __init__(self, value):
self.value = value # 类 Exception 默认的 __init__() 被覆盖
def __str__(self):
return repr(self.value)
-------------------------------------------------------
>>> try:
raise MyError(2*2)
except MyError as e:
print('My exception occurred, value:', e.value)
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
- 自定义 de "异常基类"
当创建一个模块有可能抛出多种不同的异常时,一种通常的做法是:
(1) 为这个包建立一个基础异常类;
(2) 然后基于这个基础类为不同的错误情况创建不同的子类。
# 有点像接口的感觉
class Error(Exception):
"""Base class for exceptions in this module."""
pass
------------------------------------------------------------------ # 再定义具体的异常内容
class InputError(Error):
"""Exception raised for errors in the input. Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
""" def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""Raised when an operation attempts a state transition that's not
allowed. Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is not allowed
""" def __init__(self, previous, next, message):
self.previous = previous
self.next = next
self.message = message
End.
[Python] 07 - Statements --> Functions的更多相关文章
- Think Python - Chapter 03 - Functions
3.1 Function callsIn the context of programming, a function is a named sequence of statements that p ...
- 【Python注意事项】如何理解python中间generator functions和yield表情
本篇记录自己的笔记Python的generator functions和yield理解表达式. 1. Generator Functions Python支持的generator functions语 ...
- 【Python 07】汇率兑换1.0-2(基本元素)
1.Python基本元素 (1)缩进:表示代码层次关系(Python中表示程序框架唯一手段) 1个tab或者4个空格 (2)注释:开发者加入的说明信息,不被执行.一个代码块一个注释. # 单行注释(一 ...
- python - 6. Defining Functions
From:http://interactivepython.org/courselib/static/pythonds/Introduction/DefiningFunctions.html Defi ...
- 46 Simple Python Exercises-Higher order functions and list comprehensions
26. Using the higher order function reduce(), write a function max_in_list() that takes a list of nu ...
- python 07 字典 集合
字典 key:value 键:值 映射,哈希值,关系(数组) x=['1','2'] y=['a','b'] >>print(y[x.index('1')]) a index() 函数用于 ...
- python 07篇 内置函数和匿名函数
一.内置函数 # 下面这些要掌握 # len type id print input open # round min max filter map zip exec eval print(all([ ...
- python 07
1.文件操作: f=open(...) 是由操作系统打开文件,那么如果我们没有为open指定编码,那么打开文件的默认编码很明显是操作系统说了算了 操作系统会用自己的默认编码去打开文件,在windows ...
- headfirst python 07 ~ 08
Web 不论你在 web 上做什么, 都离不开请求和响应, web请求作为某个用户交互的结果由web浏览器发送到web服务器, 在web服务器上, 会生成web响应(或应答)并发回到 web 浏览器. ...
随机推荐
- 本地Sql Server数据库传到服务器数据库
将网站项目上传到服务器时,会遇到本地数据库该如何上传的问题.下面在西部数码购买的虚拟主机的基础上,解决数据库上传问题. 1.在西部数码购买虚拟主机后,会赠送了一个数据库,该数据库就可以作为网站项目 ...
- __x__(2)0905第二天__计算机软件和硬件
计算机(Computer)由硬件和软件组件,没有软件的计算机称为 裸机, 计算机的软件包括操作系统(OS)和应用软件(Software). 操作系统(Operating System,简称OS) 是管 ...
- C++中extern “C”含义及extern、static关键字浅析
https://blog.csdn.net/bzhxuexi/article/details/31782445 1.引言 C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C ...
- JAVA递归、非递归遍历二叉树(转)
原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...
- webservice-整理
webservice-整理 RPC与WebService的区别:https://blog.csdn.net/defonds/article/details/71641634 http://www.di ...
- Vim命令相关
在shell中,记住一些常用的vim命令,会在操作时候事半功倍. 光标移动 h,j,k,l,h #表示往左,j表示往下,k表示往右,l表示往上 Ctrl f #上一页 Ctrl b #下一页 w, e ...
- Spark GraphX实例(3)
7. 图的聚合操作 图的聚合操作主要的方法有: (1) Graph.mapReduceTriplets():该方法有一个mapFunc和一个reduceFunc,mapFunc对图中的每一个EdgeT ...
- 外网IP监测上报程序(使用Poco库的SMTPClientSession发送邮件)
目录 IPReport 项目介绍 编译说明 安装使用说明 获取外网IP方式 邮件发送关键代码 IPReport 代码地址https://gitee.com/solym/IPReport 项目介绍 外网 ...
- grep与孪生兄弟egrep差异
egrep是对grep的功能扩展,让其支持正则更加完美! #grep与egrep不同 egrep完全支持正则 ls |grep -i '[a-z]\{3\}' === ls |egrep -i ...
- 阿里云日志服务采集自建Kubernetes日志(标准输出日志)
日志服务支持通过Logtail采集Kubernetes集群日志,并支持CRD(CustomResourceDefinition)进行采集配置管理.本文主要介绍如何安装并使用Logtail采集Kuber ...