Вводящая в заблуждение глобальная переменная
У меня есть упражнение, и теперь оно хорошо и работает. Алгоритм этого упражнения - загрузить файлы с FTP-сервера, сжать их и снова загрузить в папку загрузки на FTP-сервере. Кстати это мой код:
import os
import zipfile
import ConfigParser
import ftputil
import shutil
import tempfile
import time
def download_files():
# Alvin: Think of a better variable name
file_list = ftp.listdir(downloads)
for filename in file_list:
# Alvin should be ftp.path.join
source = os.path.join(downloads, filename)
target = os.path.join(temp_path, filename)
ftp.download(source, target)
def zipping_files():
dirlist = os.listdir(temp_path)
global filepath
filepath = os.path.join(temp_path, 'part2b.zip')
# Alvin: find better name for zip_name
global zip_name
zip_name = zipfile.ZipFile(filepath, 'w')
# Alvin: try not to use built-in names as variable names
for list in dirlist:
# Alvin: file_path, absolute_path
get_file = os.path.join(temp_path, list)
zip_name.write(get_file, 'part2b\\' + list)
def upload_files():
ftp.upload(filepath, uploads + '/part2b.zip')
def main():
# Alvin: Do not use globals.
# Alvin: remove useless and above all misleading comments
global temp_path
temp_path = tempfile.mkdtemp()
config = ConfigParser.ConfigParser()
config.readfp(open('config.ini'))
server = config.get('main', 'Server')
username = config.get('main', 'Username')
password = config.get('main', 'Password')
global uploads
uploads = config.get('main', 'Upload folder')
global downloads
downloads = config.get('main', 'Download folder')
#connect to ftp
global ftp
# Alvin: open ftp connection when you only need it.
ftp = ftputil.FTPHost(server, username, password)
try:
download_files()
zipping_files()
upload_files()
finally:
ftp.close()
zip_name.close()
shutil.rmtree(temp_path, 'true')
print "Successfully transferred files."
print ""
print "This will close in 5 seconds. . . . ."
time.sleep(5)
if __name__ == '__main__':
main()
Хорошо, соглашения об именах оставляют это для меня. Но я хочу попросить привести пример того, как я могу перекодировать его, не используя все мои глобальные переменные? Спасибо за вашу помощь!
4 ответа
Вы должны точно указать параметры для ваших методов.
Вот как вы можете продолжить:
- Определите, что делает ваш метод, каждый должен иметь только одну точную цель
- Определите, что для этого нужно, и внесите в список аргументов.
- Определите, что он должен вернуть и вернуть его
- Ваша основная функция в порядке, и это идеальное место для централизации этих переменных
Один из самых лучших моментов неиспользования глобальных переменных и наличия одного метода / одного метода - это то, что будет очень легко тестировать / отлаживать, когда что-то пойдет не так.
Пример:
ваш download_files()
требует downloads
а также temp_path
аргументы, а не глобальные
У вас есть четыре глобала в вашем main
по состоянию на сейчас. (temp_path, uploads, downloads
а также ftp
).
Сначала удалите (закомментируйте) строки с глобальным, а затем эти 4 переменные станут локальными для main
, Но это сделало бы их недоступными для других функций. Итак, вам нужно передать их в функции.
Для этого вы можете добавить эти переменные в качестве параметров к функциям, которые их требуют. Так, например, вы try
блок изменится на..
try:
download_files(downloads, temp_path)
zipping_files(temp_path)
upload_files(ftp)
Теперь, чтобы соответствовать добавлению параметров, вы также должны изменить функции. Подписи функций, которые вызываются из try
блок будет:
def download_files(downloads, temp_path):
def zipping_files(temp_path):
def upload_files(ftp):
Точно так же вы можете удалить глобальные переменные, которые есть в других функциях (например, global filepath
в zipping_files()
).
Спасибо, ребята, за вашу помощь! Все ваши ответы очень полезны! Это мой последний и работающий код:
import os
import zipfile
import ConfigParser
import ftputil
import shutil
import tempfile
import time
def download_files(downloads, temp_path, server, username, password):
ftp = ftputil.FTPHost(server, username, password)
file_list = ftp.listdir(downloads)
for filename in file_list:
source = os.path.join(downloads, filename)
target = os.path.join(temp_path, filename)
ftp.download(source, target)
ftp.close()
def zipping_files(temp_path):
file_list = os.listdir(temp_path)
filepath = os.path.join(temp_path, 'part2b.zip')
zip_file = zipfile.ZipFile(filepath, 'w')
for filename in file_list:
file_path = os.path.join(temp_path, filename)
zip_file.write(file_path, 'part2b\\' + filename)
zip_file.close()
def upload_files(uploads, temp_path, server, username, password):
ftp = ftputil.FTPHost(server, username, password)
filepath = os.path.join(temp_path, 'part2b.zip')
ftp.upload(filepath, uploads + '/part2b.zip')
ftp.close()
def main():
temp_path = tempfile.mkdtemp()
config = ConfigParser.ConfigParser()
config.readfp(open('config.ini'))
server = config.get('main', 'Server')
username = config.get('main', 'Username')
password = config.get('main', 'Password')
uploads = config.get('main', 'Upload folder')
downloads = config.get('main', 'Download folder')
try:
download_files(downloads, temp_path, server, username, password)
zipping_files(temp_path)
upload_files(uploads, temp_path, server, username, password)
finally:
shutil.rmtree(temp_path, 'true')
print "Successfully transferred files."
print ""
print "This will close in 5 seconds. . . . ."
time.sleep(5)
if __name__ == '__main__':
main()
Любые предложения все будут рассмотрены! Еще раз спасибо!
Python на самом деле не поддерживает глобальные переменные - они вообще плохая идея. Либо передайте данные в качестве аргументов, либо заключите ваши функции в класс и используйте параметры в качестве членов класса.
(Технически, у python есть глобальное ключевое слово, но вы должны использовать его в каждой функции для каждой глобальной переменной. Это ужасно, как и должно быть. Не используйте его.)