我给了一些ISBN号,例如3-528-03851
(无效),3-528-16419-0
(有效)。我应该写一个测试ISBN号是否有效的程序。
这是'我的代码:
def check(isbn):
check_digit = int(isbn[-1])
match = re.search(r'(\d)-(\d{3})-(\d{5})', isbn[:-1])
if match:
digits = match.group(1) + match.group(2) + match.group(3)
result = 0
for i, digit in enumerate(digits):
result += (i + 1) * int(digit)
return True if (result % 11) == check_digit else False
return False
我使用正则表达式来检查a)格式是否有效以及b)提取ISBN字符串中的数字。虽然它似乎有用,但作为一名Python初学者,我很想知道如何改进我的代码。建议?
答案 0 :(得分:15)
首先,尽量避免这样的代码:
if Action():
lots of code
return True
return False
将其翻转,因此大量代码不会嵌套。这给了我们:
def check(isbn):
check_digit = int(isbn[-1])
match = re.search(r'(\d)-(\d{3})-(\d{5})', isbn[:-1])
if not match:
return False
digits = match.group(1) + match.group(2) + match.group(3)
result = 0
for i, digit in enumerate(digits):
result += (i + 1) * int(digit)
return True if (result % 11) == check_digit else False
代码中存在一些错误:
所以,让我们简化一下。首先,删除所有空格和连字符,并通过在'^ ... $'中支持它来确保正则表达式与整行匹配。这可以确保它拒绝太长的字符串。
def check(isbn):
isbn = isbn.replace("-", "").replace(" ", "");
check_digit = int(isbn[-1])
match = re.search(r'^(\d{9})$', isbn[:-1])
if not match:
return False
digits = match.group(1)
result = 0
for i, digit in enumerate(digits):
result += (i + 1) * int(digit)
return True if (result % 11) == check_digit else False
接下来,让我们修复“X”校验位问题。匹配正则表达式中的校验位,因此整个字符串由正则表达式验证,然后正确转换校验位。
def check(isbn):
isbn = isbn.replace("-", "").replace(" ", "").upper();
match = re.search(r'^(\d{9})(\d|X)$', isbn)
if not match:
return False
digits = match.group(1)
check_digit = 10 if match.group(2) == 'X' else int(match.group(2))
result = 0
for i, digit in enumerate(digits):
result += (i + 1) * int(digit)
return True if (result % 11) == check_digit else False
最后,使用生成器表达式和max
是在Python中进行最终计算的更惯用的方法,并且可以简化最终条件。
def check(isbn):
isbn = isbn.replace("-", "").replace(" ", "").upper();
match = re.search(r'^(\d{9})(\d|X)$', isbn)
if not match:
return False
digits = match.group(1)
check_digit = 10 if match.group(2) == 'X' else int(match.group(2))
result = sum((i + 1) * int(digit) for i, digit in enumerate(digits))
return (result % 11) == check_digit
答案 1 :(得分:3)
毫无意义的改进:将return True if (result % 11) == check_digit else False
替换为return (result % 11) == check_digit
答案 2 :(得分:3)
完成确认后检查一下:)
http://www.staff.ncl.ac.uk/d.j.wilkinson/software/isbn.py
和
http://chrisrbennett.com/2006/11/isbn-check-methods.html
编辑:很抱歉让我感到困惑,我没有看到家庭作业标签,但也许在完成作业后你可以看到其他人之前做过的事情,我想你可以从别人的代码中学到很多东西;对不起:(
答案 3 :(得分:3)
check_digit
初始化可以引发ValueError
。为什么不用正则表达式而不是使用切片来取出校验位?$
锚定结尾,但在你的情况下,因为你的正则表达式是固定宽度的,所以无关紧要。)''.join(match.groups())
,然后再将check_digit
拉出来,而不是手动列出论坛。您可以在将其转换为int
之前进行转换,因为您希望将它们全部转换为int
。sum()
来添加元素。True if (expression) else False
通常可以简单地替换为expression
。同样,False if (expression) else True
始终可以简单地替换为not expression
把所有这些放在一起:
def check(isbn):
match = re.match(r'(\d)-(\d{3})-(\d{5})-(\d)$', isbn)
if match:
digits = [int(x) for x in ''.join(match.groups())]
check_digit = digits.pop()
return check_digit == sum([(i + 1) * digit
for i, digit in enumerate(digits)]) % 11
return False
最后一行可以说是不必要的,因为默认行为是返回None(这是假的),但是从某些路径而不是从其他路径的显式返回对我来说看起来像一个bug,所以我认为离开时更容易理解它在。
答案 4 :(得分:2)
如果您属于isbn.org合规性检查机构,所有正则表达式的内容都很棒。
但是,如果您想知道潜在客户在浏览器中输入的内容是否值得查询您的待售图书数据库,那么您不希望所有这些漂亮的红色制服。简单地扔掉除了0-9和X之外的所有东西......哦,是的,没有人使用移位键,所以我们也更好地允许x。然后,如果它的长度为10并通过校验位测试,则值得进行查询。
来自http://www.isbn.org/standards/home/isbn/international/html/usm4.htm
校验位是最后一位数字 一个ISBN。它是根据模数计算的 11用权重10-2,用X代替 10,其中十个将作为支票发生 数字。
这意味着前九个中的每一个 ISBN的数字 - 不包括 校验数字本身 - 乘以 数字从10到2不等 由此产生的产品总和, 加上校验位,必须是 可以被11整除而没有余数。
这是一种非常啰嗦的说法,“所有数字中的每一个都乘以10到1之间的数字,并且产生的产品总和必须可以被11整除而没有余数”
def isbn10_ok(s):
data = [c for c in s if c in '0123456789Xx']
if len(data) != 10: return False
if data[-1] in 'Xx': data[-1] = 10
try:
return not sum((10 - i) * int(x) for i, x in enumerate(data)) % 11
except ValueError:
# rare case: 'X' or 'x' in first 9 "digits"
return False
tests = """\
3-528-03851
3-528-16419-0
ISBN 0-8436-1072-7
0864425244
1864425244
0864X25244
1 904310 16 8
0-473-07480-x
0-473-07480-X
0-473-07480-9
0-473-07480-0
123456789
12345678901
1234567890
0000000000
""".splitlines()
for test in tests:
test = test.strip()
print repr(test), isbn10_ok(test)
输出:
'3-528-03851' False
'3-528-16419-0' True
'ISBN 0-8436-1072-7' True
'0864425244' True
'1864425244' False
'0864X25244' False
'1 904310 16 8' True
'0-473-07480-x' True
'0-473-07480-X' True
'0-473-07480-9' False
'0-473-07480-0' False
'123456789' False
'12345678901' False
'1234567890' False
'0000000000' True
'' False
旁白:一个着名的大型图书销售网站将接受047307480x,047307480X和0-473-07480-X但不接受0-473-07480-x :-O
答案 5 :(得分:1)
你的代码很好 - 为编写惯用的Python做得很好!以下是一些小问题:
当你看到成语
时result = <initiator>
for elt in <iterable>:
result += elt
您可以通过列表理解来替换它。在这种情况下:
result = sum((i+1)*int(digit) for i, digit in enumerate(digits)
或者更简洁:
return sum((i+1)*int(digit) for i, digit in enumerate(digits) % 11 == check_digit
当然,这是一个价值判断,这是否比原来更好。我个人认为其中第二个是最好的。
另外,(result % 11) == check_digit
中的额外括号是无关紧要的,为了清楚起见,我并不认为你需要它们。总的来说,你可以:
def validate(isbn):
check_digit = int(isbn[-1])
match = re.search(r'(\d)-(\d{3})-(\d{5})', isbn[:-1])
if match:
digits = match.group(1) + match.group(2) + match.group(3)
parity = sum((i+1)*int(digit) for i, digit in enumerate(digits)
return parity % 11 == check_digit
else:
return False
请注意,您仍然需要使用return False
来检测ISBN的格式是否正确。
答案 6 :(得分:1)
不要忘记(尽管这可能超出了您的作业范围)来计算ISBN的校验位(最后一位数),以确定ISBN是否有效而不是只是看似有效。
有一些关于校验位on the ISBN.org website的实现的信息,实现应该相当简单。 Wikipedia提供了一个这样的例子(假设你已经将任何ASCII“X”转换为十进制10):
bool is_isbn_valid(char digits[10]) {
int i, a = 0, b = 0;
for (i = 0; i < 10; i++) {
a += digits[i]; // Assumed already converted from ASCII to 0..10
b += a;
}
return b % 11 == 0;
}
将这个用于你的作业留下来,好吧,作为练习。
答案 7 :(得分:1)
您的校验位可以取0-10的值,基于它的模数为11的事实。该行存在问题:
check_digit = int(isbn[-1])
因为这仅适用于数字0-9。当数字为'X'时,你需要一些东西,如果不是上述任何一种,你也需要错误的情况 - 否则你的程序会崩溃。