我的任务是使用Python将一些JPG图像转换为PNG格式。我成功做到了,但是我想知道是否可以使我的代码比我现在的代码更好。

代码-

#Python Code

import os  
from PIL import Image  

path1 = sys.argv[1]  
path2 = sys.argv[2]  

if not os.path.exists(path2):
    os.makedirs(path2)  

for filename in os.listdir(path1):
    if filename.endswith(".jpg"):
        # print(filename.split(".")[0])
        im = Image.open(f"{path1}{filename}")
        # print(Image.open(f"{path1}{filename}"))
        im.save(f"{path2}/{filename.split('.')[0]}.png", "png")
        print("Lets go")


评论

欢迎来到代码审查。我回滚了您的上一次编辑。请不要更新问题中的代码以合并答案的反馈,这与代码审查的“问题+答案”风格背道而驰。这不是您应该在其中保留最新版本的论坛。收到答案后,请查看您可能会做什么和可能不会做什么。

根据您的用例和预期的用户群,考虑是否有人可以尝试使用python test.py foo / img.jpg bar / img.png或python test.py in / out /(其中存在in / foo.jpeg)。

略有切线,可能值得注意的是,将JPEG图像转换为PNG格式而不进行清理以消除压缩伪像几乎几乎没有实际用处。它只会使文件(可能很多次)变大而不会以任何方式提高图像的质量。 (当然,在某些情况下可能会出现异常,例如,如果您尝试将图像馈送到可以解析PNG但不能解析JPEG的程序中。)

您是否真的要为每个文件打印一次(“放手”)?

我在这里比Ilmari还要走得更远,说如果我承担了这项任务,我将不得不告诉我的雇主,如果您认为您需要将JPEG转换为PNG的工具,那么您的流程几乎肯定有问题整体上您基本上是将水从1加仑的水桶倒入5加仑的水桶;您仍然只有1加仑。找出您的过程中哪些环节失去了其他四个环节,并予以消除。

#1 楼

没有太多改进,只是做一些小的整理工作。


帮助最终用户!

如果我使用此脚本,却忘了放一个或两个脚本时文件路径的数量,将出现以下错误:IndexError: list index out of range。此错误不会对用户有很大帮助。请考虑以下问题:

try:
    path1 = sys.argv[1]  
    path2 = sys.argv[2]
except IndexError:
    raise Exception("Usage: python3 jpg_to_png.py <path to input folder> <path to output folder>")


这向用户提供了有关如何使用脚本的详细消息。

删除注释的代码。

不再需要的代码会增加无用的混乱,并使您的代码难以阅读。

评论


\ $ \ begingroup \ $
谢谢Linny,我将通过使用您的建议来改进代码,并感谢您对注释掉的代码的建议。
\ $ \ endgroup \ $
– Praguru 14
20-2-12在4:10

\ $ \ begingroup \ $
@Praguru有人回答您的问题后,您不应该编辑代码。您可以回滚您的编辑以使他们的answrr有效为agian吗?
\ $ \ endgroup \ $
–S.S. Anne
20-2-12在4:27

\ $ \ begingroup \ $
更好的方法是只使用argparse库。
\ $ \ endgroup \ $
– AleksandrH
20-2-12在14:36

\ $ \ begingroup \ $
每次进行argparse投票
\ $ \ endgroup \ $
–莫格说要恢复莫妮卡
20-2-13在12:53

\ $ \ begingroup \ $
尽管argparse很方便,但使用起来却很痛苦,对于完成一项任务的小型项目,它只会造成imo的障碍。
\ $ \ endgroup \ $
–以前
20-2-14在13:21

#2 楼

变量名称path1path2不好,因为它们的描述性不强。它们应该是srcdirdstdir(如果您更喜欢缩写),或者如果要拼写它们,则应该是source_directorydestination_directory

而不是操纵字符串,最好使用pathlib库,该库具有方便的功能with_suffix

您应该测试具有多个点的文件名会发生什么,以防止丢失部分文件名而引起意外。

#3 楼

除了已经提到的内容外,我想指出的是,
文件super.picture.jpg将被转换为super.png

如果有人循环运行您的程序并遍历文件夹,这可能是一个问题带有名为anniversary.1.jpg的文件q.4312079q ....

相反,因为您使用过anniversary.2.jpg,所以只能使用长度为endswith('.jpg')的子字符串。

评论


\ $ \ begingroup \ $
这不适用于.jpeg。与其使用子字符串,不如考虑使用os.path.splitext。它可以正确说明此用例,甚至更一般的用例。 OP可以扩展此脚本,以将指定格式的所有图像转换为另一种格式。另外,也可以只搜索字符串末尾的第一个句点,然后在其中分割。但是最好使用现有的库。
\ $ \ endgroup \ $
– AleksandrH
20-2-12在16:53



#4 楼

考虑使用__main__使脚本更易于导入(请参阅此处)。

此外,请考虑使用函数来隔离不同的步骤,以便将来更容易添加新功能。 br />
最后一件事,您可以使用更明确的日志消息,类似于Converting {filename} from jpg to png而不是Lets go,也许可以使用logging模块而不是简单的打印。

评论


\ $ \ begingroup \ $
我怀疑其意图是将此脚本导入其他Python源文件中。这听起来更像一个独立的脚本。
\ $ \ endgroup \ $
– AleksandrH
20-2-12在14:37

\ $ \ begingroup \ $
@AleksandrH表示同意,但是,如果没有过度使用,则为这种将来不可预见的用例做准备也不会有害,例如在这种情况下。另外,获得更好的模块化和体系结构始终是一个好习惯。
\ $ \ endgroup \ $
–bracco23
20-2-12在14:40

\ $ \ begingroup \ $
是的,这很公平。仅将所有内容包装在__main__检查中并不需要太多的努力。
\ $ \ endgroup \ $
– AleksandrH
20-2-12在16:56

#5 楼

函数

我将其抽象为可以调用的函数。然后,您还可以轻松地进行一些检查,以查看源路径是否确实是目录,目的地路径是否确实存在,......

pathlib.Path

有很多有助于文件路径处理的便捷方法。

from PIL import Image
from pathlib import Path
def convert_jpg_to_png(
    source: Path, 
    destination: Path, 
    overwrite=False,
) -> None:
    if not source.is_dir():
        raise ValueError(f"{source} is not a directory")
    if not source.exists():
        raise ValueError(f"{source} does not exist")
    if not destination.is_dir():
        raise ValueError(f"{destination} is not a directory")
    if not destination.exists():
        destiny.mkdir(parents=True)

    for source_name in source.glob("**/*.jpg"):
#     If you want to cover both jpg and jpeg, 
#     for source_image in source.glob("**/*"):
#         if not source_image.suffix.lower in {".jpg", ".jpeg"}:
#             continue
        destination_name = destination / (
            source_name.relative_to(source.with_suffix(".png"))
        )
        if destination_name.exists() and not overwrite:
            print(f"{destination_name} already exists")
            continue
        print(f"renaming {source_name} to {destination_name}")
        with Image.open(source_name) as im:
            im.save(destination_name, format="PNG")


如果您还想转换.JPG,.JPEG和.jpeg,

主后卫

然后可以将您的逻辑隐藏在if __name__ == "__main__":后卫后面,以便以后可以在需要的其他程序中导入此模块:

if __name__ == "__main__":
    if len(sys.argv) == 1:
        source = Path(".") # default: current location
        destination = source
        overwrite = False
    source = Path(sys.argv[1])
    if len(sys.argv) > 2:
        if sys.argv[2] in {"True", "False"}:
            destination = source
            overwrite = sys.argv[2] == "True"
        else:
            destination = Path(sys.argv[2])
    else:
        destination = source
        overwrite = False
    if len(sys.argv) > 3:
        overwrite = sys.argv[3] == "True"
    convert_jpg_to_png(source, destination, overwrite)


我在这里手动处理了参数,但是您可以更好地检查argparse模块来简化此操作

#6 楼

通常,代码简单明了。

取决于目标用途,除了其他答案建议之外,我还要添加:


对文件结尾的支持在“。jpeg”上不仅是.jpg;
同样,我将使文件名过滤器还查找大写或大小写混合的JPEG文件;
如果我不得不处理不可靠的源,我还将添加Image.verify()和/或imghdr检查是否获得了有效的jpeg文件,否则给出有意义的错误消息。


#7 楼

我建议您执行以下操作:



可以使用生成器表达式过滤文件列表来删除嵌套级别:

files_to_convert = (f for f in os.listdir(path1) if f.endswith(".jpg"))
for filename in files_to_convert:
    ... process the file ...



确保*.jpg的列表是文件,而不是名为*.jpg的子目录:

files_to_convert = (f for f in os.listdir(path1) if f.endswith(".jpg") and os.path.isfile(f))



使用os.path.join构造路径:

im = Image.open(os.path.join(path1, filename))



使用os.path.splitext拆分文件名:

root, _ = os.path.splitext(filename)
png_file = os.path.join(path2, f'{root}.png')
im.save(png_file, "png")