众所周知,在Python中其实并没有一个严格定义的常量类概念。

目前所采用的常用约定俗成的方式是采用命名全为大写字母的方式来标识别常量。

但实际上这种方式并不能起到防止修改的功能,而只是从语义和可读性上做了区分。

现已有了一种基于__setter__和__delattr__的实现方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# coding:utf-8
import sys


class _const:
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
orig = super(_const, cls)
cls._instance = orig.__new__(cls, *args, **kw)
return cls._instance

# 已存在
class ConstBuiltError(TypeError):
def __init__(self, name):
self.msg = "Can't rebind const instance attribute (%s)" % name

def __str__(self):
return 'error msg: {}'.format(self.msg)

# 非全大写错误(可下划线)
class ConstCaseError(TypeError):
def __init__(self, name):
self.msg = 'const name "%s" is not all uppercase' % name
def __str__(self):
return 'error msg: {}'.format(self.msg)

def __repr__(self):
return self.__str__()

# 删除错误
class ConstDelError(TypeError):
def __init__(self, name):
self.msg = "Can't delete const instance attribute (%s)" % name

def __str__(self):
return 'error msg: {}'.format(self.msg)

def __repr__(self):
return self.__str__()

# 创建时核对是否重复或全大写
def __setattr__(self, name, value):
if self.__dict__.__contains__(name):
raise self.ConstBuiltError(name)
if not name.isupper():
raise self.ConstCaseError(name)
self.__dict__[name] = value

# 禁止删除
def __delattr__(self, name):
if self.__dict__.__contains__(name):
raise self.ConstDelError(name)
raise self.ConstDelError(name)


# 实例化一个类
Const = _const()
Const.TEST = 'test'

假设文件保存为constClass使用的时候只要from constClass import Const,便可以直接定义常量了,比如:

1
2
3
4
5
6
from constClass import Const
print(Const.TEST) # 已有定义
Const.AUTHOR = 'smile' # 首次定义
Const.AUTHOR = 'smilelc' # 修改
Const.author = 'smile' # 小写定义
del Const.AUTHOR # 删除
  1. 已有定义时,如Const.TEST = 'test',可直接调用;
  2. 上面的Const.AUTHOR定义后便不可再更改,因此Const.AUTHOR = ‘smilelc’会抛出ConstBuiltError异常;
  3. 而常量名称如果小写,如Const.author ='smile',也会抛出ConstCaseError异常;
  4. 一旦定义完后,若删除,会抛出ConstDelError

DNS

域名系统(英文:Domain Name System,缩写:DNS)是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用TCPUDP端口53。当前,对于每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符。

A记录(主机记录)

A(Address)记录是用来指定主机名(或域名)对应的IP地址记录。用户可以将该域名下的网站服务器指向到自己的web server上。

以域名liuchang.men为例,添加A记录:

类型 名称
A proxy 47.101.212.137

proxy.liuchang.men是指定域名对应的IP地址47.101.212.137。

A记录同时也可以设置域名的二级域名,如:

类型 名称
A *.proxy 47.101.212.137

使用通配符*泛解析所有 *.proxy.liuchang.men 指向IP地址47.101.212.137。

CNAME记录(别名记录)

CNAME(Canonical Name )别名记录,允许您将多个名字映射到同一台计算机。通常用于同时提供WWW和MAIL服务的计算机。例如:

类型 名称
CNAME www smilelc3.github.io
CNAME mail ym.163.com
CNAME @ smilelc3.github.io

若有一台计算机名为smilelc3.github.io(A记录),它能提供WWW服务,而另一台机器名为ym.163.com能提供mail服务,我希望www.liuchang.men能够指向smilelc3.github.io,而mail.liuchang.men能够指向ym.163.com,则记录值如上。

  • 注意:记录值留白或使用@符代表使用域名自身作为名称,上表第三条中,@代表liuchang.men指向smilelc3.github.io域名。

AAAA记录(IPv6主机记录)

AAAA 记录是用来指定主机名(或域名)对应的IPv6地址记录。

TXT记录

TXT记录一般是为某条记录设置说明,用来保存域名的附加文本信息,TXT记录的内容按照一定的格式编写,最常用的是SPF(Sender Policy Framework)格式。反垃圾邮件是TXT的应用之一,SPF是跟DNS相关的一项技术,它的内容写在DNS的TXT类型的记录里面。

在命令行下可以使用如下命令来查看域名liuchang.men的TXT记录。

1
nslookup -qt=txt liuchang.men

MX记录

MX记录也叫做邮件路由记录,用户可以将该域名下的邮件服务器指向到自己的mail server上,然后即可自行操控所有的邮箱设置。您只需在线填写您服务器的IP地址,即可将您域名下的邮件全部转到您自己设定相应的邮件服务器上。MX记录的作用是给寄信者指明某个域名的邮件服务器有哪些,SPF格式的TXT记录的作用跟MX记录相反,它向收信者表明,哪些邮件服务器是经过某个域名认可发送邮件的。

DS记录

NS(Name Server)记录是域名服务器记录,用来指定该域名由哪个DNS服务器来进行解析。 您注册域名时,总有默认的DNS服务器,每个注册的域名都是由一个DNS域名服务器来进行解析的,DNS服务器NS记录地址一般以以下的形式出现: ns1.domain.com、ns2.domain.com等。简单的说,NS记录是指定由哪个DNS服务器解析你的域名。

URL记录

将域名指向一个http(s)协议地址,访问域名时,自动跳转至目标地址。

问题要求:给你一个字符串,这个字符串表示一个表达式,这个表达式可能有整数/小数,加减乘除符号和小括号,求这个表达式的值。

三种算术表达式

算术表达式中最常见的表示法形式有 中缀前缀后缀表示法。中缀表示法是书写表达式的常见方式,而前缀和后缀表示法主要用于计算机科学领域。

中缀表示法

中缀表示法是算术表达式的常规表示法。称它为 中缀表示法是因为每个操作符都位于其操作数的中间,这种表示法只适用于操作符恰好对应两个操作数的时候(在操作符是二元操作符如加、减、乘、除以及取模的情况下)。对以中缀表示法书写的表达式进行语法分析时,需要用括号和优先规则排除多义性。

1
(A+B)*C-D/(E+F)

前缀表示法

前缀表示法中,操作符写在操作数的前面。这种表示法经常用于计算机科学,特别是编译器设计方面。为纪念其发明家 ― Jan Lukasiewicz(请参阅参考资料,这种表示法也称 波兰表示法

1
-*+ABC/D+EF

后缀表示法

在后缀表示法中,操作符位于操作数后面。后缀表示法也称 逆波兰表示法(reverse Polish notation,RPN),因其使表达式求值变得轻松,所以被普遍使用。

1
AB+C*DEF+/-

中缀表达式到后缀表达式的转换

要把表达式从中缀表达式的形式转换成用后缀表示法表示的等价表达式,必须了解操作符的优先级和结合性。 优先级或者说操作符的强度决定求值顺序;优先级高的操作符比优先级低的操作符先求值。 如果所有操作符优先级一样,那么求值顺序就取决于它们的 结合性。操作符的结合性定义了相同优先级操作符组合的顺序(从右至左或从左至右)。

1
2
Left associativity  : A+B+C = (A+B)+C
Right associativity : A^B^C = A^(B^C)

转换过程包括用下面的算法读入中缀表达式的操作数、操作符和括号:

  1. 初始化一个空堆栈,将结果字符串变量置空。
  2. 从左到右读入中缀表达式,每次一个字符。
  3. 如果字符是操作数,将它添加到结果字符串。
  4. 如果字符是个操作符,弹出(pop)操作符,直至遇见开括号(opening parenthesis)、优先级较低的操作符或者同一优先级的右结合符号。把这个操作符压入(push)堆栈。
  5. 如果字符是个开括号,把它压入堆栈。
  6. 如果字符是个闭括号(closing parenthesis),在遇见开括号前,弹出所有操作符,然后把它们添加到结果字符串。
  7. 如果到达输入字符串的末尾,弹出所有操作符并添加到结果字符串。

后缀表达式求值

对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,而且操作符的优先级也不再起作用了。您可以用如下算法对后缀表达式求值:

  1. 初始化一个空堆栈
  2. 从左到右读入后缀表达式
  3. 如果字符是一个操作数,把它压入堆栈。
  4. 如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。
  5. 到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <stack>
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
#include <tuple>
#include <cmath>
#include <sstream>

using namespace std;

//定义栈数据结构体
struct stackData {
char Operator;
double Number;
};

inline bool isOperator(char ch) {
return ch == '+'
or ch == '-'
or ch == '*'
or ch == '/'
or ch == '^';
}


inline bool isNumber(char ch) {
return '0' <= ch and ch <= '9' or ch == '.';
}


//优先级判定
inline int priority(char operatorChar) {
int level = 0; // level越大,优先级越高
if (operatorChar == '^') {
level = 2;
} else if (operatorChar == '*' or operatorChar == '/') {
level = 1;
} else if (operatorChar == '+' or operatorChar == '-') {
level = 0;
}
return level;
}

//获取数字栈顶双数
template<typename T>
tuple<T, T> getTwoNums(stack<T> &nums) {
auto second = nums.top();
nums.pop();
auto first = nums.top();
nums.pop();
return {first, second};
} // return {first, second}

//计算后缀表达式
double postfixCalculate(vector<stackData> &postfix) {
double first, second;
stack<double> nums;
for (const auto &p : postfix) {
switch (p.Operator) {
case '*':
tie(first, second) = getTwoNums(nums);
nums.push(first * second);
break;
case '/':
tie(first, second) = getTwoNums(nums);
nums.push(first / second);
break;
case '+':
tie(first, second) = getTwoNums(nums);
nums.push(first + second);
break;
case '-':
tie(first, second) = getTwoNums(nums);
nums.push(first - second);
break;
case '^':
tie(first, second) = getTwoNums(nums);
nums.push(pow(first, second));
break;
default:
nums.push(p.Number);
break;
}
}
double result = nums.top();
nums.pop();
return result;
}

//做分割
vector<stackData> getSeparate(string &infix) {
vector<stackData> postfix;
string numStr; // 单个连续字符的数字
for (const auto &p : infix) {
if (isNumber(p)) {
numStr += p;
} else if (isOperator(p) or p == '(' or p == ')') {
if (not numStr.empty()) {
postfix.emplace_back(stackData{' ', stod(numStr)});
}
numStr = "";
postfix.emplace_back(stackData{p, 0});
}
}
if (not numStr.empty()) {
postfix.emplace_back(stackData{' ', stod(numStr)});
}

//前导缺损+-符号补0
vector<stackData> newPostfix;
char preChar = '(';
for (const auto &p : postfix) {
if (p.Operator != ' ') {
if (preChar == '(' and (p.Operator == '-' or p.Operator == '+'))
newPostfix.emplace_back(stackData{' ', 0});
preChar = p.Operator;
} else {
preChar = ' ';
}
newPostfix.emplace_back(p);
}
return newPostfix;
}

//表达式输出
string printExpression(vector<stackData> &temp) {
stringstream ss;
for (const auto &t: temp) {
if (t.Operator != ' ') {
ss << t.Operator;
} else {
ss << t.Number;
}
ss << ' ';
}
return ss.str();
}

//后缀表达式转换
vector<stackData> getPostfixExp(vector<stackData> &infix) {
stack<char> operator_stack;
vector<stackData> postfix;
for (const auto &p: infix) {
if (isOperator(p.Operator)) {
while (not operator_stack.empty()
and isOperator(operator_stack.top())
and priority(operator_stack.top()) >= priority(p.Operator)) {
postfix.emplace_back(stackData{operator_stack.top(), 0});
operator_stack.pop();
}
operator_stack.push(p.Operator);
} else if (p.Operator == '(') {
operator_stack.push(p.Operator);
} else if (p.Operator == ')') {
while (operator_stack.top() != '(') {
postfix.push_back(stackData{operator_stack.top()});
operator_stack.pop();
}
operator_stack.pop();
} else {
postfix.push_back(p);
}

}
while (not operator_stack.empty()) {
postfix.push_back(stackData{operator_stack.top(), 0});
operator_stack.pop();
}
return postfix;
}


int main() {
cout << "please input string expression: " << endl
<< "example: " << "( 15 / 3 - 1)^2 -(8 + (0.7 - 0.2)*5.41 + 6.8)+1^0.5" << endl;
string infix;
// 读取非空行
while (getline(cin, infix)) {
infix.erase(infix.find_last_not_of(" \n\r\t") + 1);
if (not infix.empty()) {
break;
}
}

vector<stackData> expression = getSeparate(infix);
cout << "Standard expression: " << printExpression(expression) << endl;
vector<stackData> postfixExp = getPostfixExp(expression);
cout << "Postfix expression: " << printExpression(postfixExp) << endl;
double result = postfixCalculate(postfixExp);
cout << "Answer: " << setprecision(10) << result;
return 0;
}

条形码或称条码barcode)是将宽度不等的多个黑条和空白,按照一定的编码规则排列,用以表达一组信息的图形标识符。常见的条形码是由反射率相差很大的黑条(简称条)和白条(简称空)排成的平行线图案。条形码可以标出物品的生产国、制造厂家、商品名称、生产日期、图书分类号、邮件起止地点、类别、日期等信息,因而在商品流通、图书管理、邮政管理、银行系统等许多领域都得到了广泛的应用。

条形码分类

条形码按类型可分为:线性条形码矩阵(二维)条形码

code128编码

Code 128是ISO/IEC 15417:2007[1]定义的条形码规范。

Code 128条码可以对全部128个ASCII字符(包括数字、大小写字母、符号和控制符)进行编码。

code128码是广泛应用在企业内部管理、生产流程、物流控制系统方面的条码码制,由于其优良的特性在管理信息系统的设计中被广泛使用,CODE128码是应用最广泛的线性条形码制之一。

code128编码分类

  • code128 A字符集:包括大写字母、数字、常用标点符号和一些控制符。

  • code128B 字符集:包括大小写字母、数字、常用标点符号。

  • code128C 字符集: 为纯数字序列。

  • code128Auto:是将上述三种字符集最佳优化组合。

code128编码构成

一个Code 128条形码由六部分组成。

  1. 空白区域
  2. 起始标记
  3. 数据区
  4. 校验符
  5. 终止符
  6. 空白区域

code128编码表

ID Code128A Code128B Code128C BandCode 图案
0 SP SP 0 212222 11011001100
1 ! ! 1 222122 11001101100
2 2 222221 11001100110
3 # # 3 121223 10010011000
4 $ $ 4 121322 10010001100
5 % % 5 131222 10001001100
6 & & 6 122213 10011001000
7 7 122312 10011000100
8 ( ( 8 132212 10001100100
9 ) ) 9 221213 11001001000
10 * * 10 221312 11001000100
11 + + 11 231212 11000100100
12 , , 12 112232 10110011100
13 - - 13 122132 10011011100
14 . . 14 122231 10011001110
15 / / 15 113222 10111001100
16 0 0 16 123122 10011101100
17 1 1 17 123221 10011100110
18 2 2 18 223211 11001110010
19 3 3 19 221132 11001011100
20 4 4 20 221231 11001001110
21 5 5 21 213212 11011100100
22 6 6 22 223112 11001110100
23 7 7 23 312131 11101101110
24 8 8 24 311222 11101001100
25 9 9 25 321122 11100101100
26 : : 26 321221 11100100110
27 ; ; 27 312212 11101100100
28 < < 28 322112 11100110100
29 = = 29 322211 11100110010
30 > > 30 212123 11011011000
31 ? ? 31 212321 11011000110
32 @ @ 32 232121 11000110110
33 A A 33 111323 10100011000
34 B B 34 131123 10001011000
35 C C 35 131321 10001000110
36 D D 36 112313 10110001000
37 E E 37 132113 10001101000
38 F F 38 132311 10001100010
39 G G 39 211313 11010001000
40 H H 40 231113 11000101000
41 I I 41 231311 11000100010
42 J J 42 112133 10110111000
43 K K 43 112331 10110001110
44 L L 44 132131 10001101110
45 M M 45 113123 10111011000
46 N N 46 113321 10111000110
47 O O 47 133121 10001110110
48 P P 48 313121 11101110110
49 Q Q 49 211331 11010001110
50 R R 50 231131 11000101110
51 S S 51 213113 11011101000
52 T T 52 213311 11011100010
53 U U 53 213131 11011101110
54 V V 54 311123 11101011000
55 W W 55 311321 11101000110
56 X X 56 331121 11100010110
57 Y Y 57 312113 11101101000
58 Z Z 58 312311 11101100010
59 [ [ 59 332111 11100011010
60 \ \ 60 314111 11101111010
61 ] ] 61 221411 11001000010
62 ^ ^ 62 431111 11110001010
63 _ _ 63 111224 10100110000
64 NUL ` 64 111422 10100001100
65 SOH a 65 121124 10010110000
66 STX b 66 121421 10010000110
67 ETX c 67 141122 10000101100
68 EOT d 68 141221 10000100110
69 ENQ e 69 112214 10110010000
70 ACK f 70 112412 10110000100
71 BEL g 71 122114 10011010000
72 BS h 72 122411 10011000010
73 HT i 73 142112 10000110100
74 LF j 74 142211 10000110010
75 VT k 75 241211 11000010010
76 FF I 76 221114 11001010000
77 CR m 77 413111 11110111010
78 SO n 78 241112 11000010100
79 SI o 79 134111 10001111010
80 DLE p 80 111242 10100111100
81 DC1 q 81 121142 10010111100
82 DC2 r 82 121241 10010011110
83 DC3 s 83 114212 10111100100
84 DC4 t 84 124112 10011110100
85 NAK u 85 124211 10011110010
86 SYN v 86 411212 11110100100
87 ETB w 87 421112 11110010100
88 CAN x 88 421211 11110010010
89 EM y 89 212141 11011011110
90 SUB z 90 214121 11011110110
91 ESC { 91 412121 11110110110
92 FS | 92 111143 10101111000
93 GS } 93 111341 10100011110
94 RS ~ 94 131141 10001011110
95 US DEL 95 114113 10111101000
96 FNC3 FNC3 96 114311 10111100010
97 FNC2 FNC2 97 411113 11110101000
98 SHIFT SHIFT 98 411311 11110100010
99 CODEC CODEC 99 113141 10111011110
100 CODEB FNC4 CODEB 114131 10111101110
101 FNC4 CODEA CODEA 311141 11101011110
102 FNC1 FNC1 FNC1 411131 11110101110
103 StartA StartA StartA 211412 11010000100
104 StartB StartB StartB 211214 11010010000
105 StartC StartC StartC 211232 11010011100
106 Stop Stop Stop 2331112 1100011101011

code128检验位计算

(开始位对应的ID值 + 每位数据在整个数据中的位置×每位数据对应的ID值)% 103

Code128编码示例

PZ1704946715 为例,开始为code128A,开始位对应的ID为103,第1位数据P对应的ID为48,第2位数据Z对应的ID为58,依此类推,可以计算。

检验位 = (103 + 1 * 48 + 2 * 58 + 3 * 17 + 4 * 23 + 5 * 16 + 6 * 20 +7 * 25 + 8 * 20 + 9 * 22 + 10 * 23 + 11 * 17 + 12 * 21) % 103 = 61​

即检验位的ID为61。

对照编码表,PZ1704946715 编码表示为:开始位Start Code A(11010000100)+ 数据位[P(11101110110)+ Z(11101100010)+1(10011100110)+ 7(11101101110)+ 0(10011101100)+ 4(11001001110)+ 9(11100101100)+ 4(11001001110)+ 6(11001110100)+ 7(11101101110)+ 1(10011100110)+ 5(11011100100)]+ 检验位61(11001000010)+ 结束位Stop(1100011101011)

若要打印,只需将1用黑色线标出,0用白色线标出,就完成一个简单的条形码生成。

0%