python использует переменную из глобальных переменных, не определяя ее как глобальную в локальной области видимости

#python #scope

#python #область

Вопрос:

В следующем сценарии:

 def update_dict(key):
   my_dict[key] = ...


mydict = dict()
k = ...
update_dict(k)
 

my_dict не было объявлено в классе, но оно использовалось update_dict .
1- Интерпретатор позволил этому произойти. Разве python не должен выдавать ошибку при этом?
2. Какие проблемы может вызвать использование глобальной переменной без объявления ее как глобальной в локальной области видимости, в которой она используется? Использует ли python указатель на глобальную переменную на основе имени переменной?


Этот фрагмент представляет собой краткое изложение, сделанное мной, и полный код следует за этим абзацем, если вы считаете (возможно, правильно?) что я допустил ошибку в своем резюме. Эквивалент mydict здесь sites .

 import sys
import csv
import argparse
import gzip

class SiteStats:
    def __init__(self, g_size, g_seq):
        self.num_reads = 0
        self.called_sites = 0
        self.called_sites_methylated = 0
        self.group_size = g_size
        self.sequence = g_seq

def update_call_stats(key, num_called_cpg_sites, is_methylated, sequence):
    if key not in sites:
        sites[key] = SiteStats(num_called_cpg_sites, sequence)

    sites[key].num_reads  = 1
    sites[key].called_sites  = num_called_cpg_sites
    if is_methylated > 0:
        sites[key].called_sites_methylated  = num_called_cpg_sites

parser = argparse.ArgumentParser( description='Calculate methylation frequency at genomic CpG sites')
parser.add_argument('-c', '--call-threshold', type=float, required=False, default=2.0)
parser.add_argument('-s', '--split-groups', action='store_true')
args, input_files = parser.parse_known_args()
assert(args.call_threshold is not None)

sites = dict()
# iterate over input files and collect per-site stats
for f in input_files:
    if f[-3:] == ".gz":
        in_fh = gzip.open(f, 'rt')
    else:
        in_fh = open(f)
    csv_reader = csv.DictReader(in_fh, delimiter='t')
    for record in csv_reader:

        num_sites = int(record['num_motifs'])
        llr = float(record['log_lik_ratio'])

        # Skip ambiguous call
        if abs(llr) < args.call_threshold * num_sites:
            continue
        sequence = record['sequence']

        is_methylated = llr > 0

        # if this is a multi-cpg group and split_groups is set, break up these sites
        if args.split_groups and num_sites > 1:
            c = str(record['chromosome'])
            s = int(record['start'])
            e = int(record['end'])

            # find the position of the first CG dinucleotide
            sequence = record['sequence']
            cg_pos = sequence.find("CG")
            first_cg_pos = cg_pos
            while cg_pos != -1:
                key = (c, s   cg_pos - first_cg_pos, s   cg_pos - first_cg_pos)
                update_call_stats(key, 1, is_methylated, "split-group")
                cg_pos = sequence.find("CG", cg_pos   1)
        else:
            key = (str(record['chromosome']), int(record['start']), int(record['end']))
            update_call_stats(key, num_sites, is_methylated, sequence)

# header
print("t".join(["chromosome", "start", "end", "num_motifs_in_group", "called_sites", "called_sites_methylated", "methylated_frequency", "group_sequence"])
)

sorted_keys = sorted(list(sites.keys()), key = lambda x: x)

for key in sorted_keys:
    if sites[key].called_sites > 0:
        (c, s, e) = key
        f = float(sites[key].called_sites_methylated) / sites[key].called_sites
        print("%st%st%st%dt%dt%dt%.3ft%s" % (c, s, e, sites[key].group_size, sites[key].called_sites, sites[key].called_sites_methylated, f, sites[k
ey].sequence))
 

Спасибо!

Комментарии:

1. «1- Интерпретатор позволил этому произойти. Разве python не должен выдавать ошибку при этом? » Нет, с чего бы это? Когда возникнет ошибка и почему?

2. «2- Какие проблемы могут возникнуть при использовании глобальной переменной без объявления ее как глобальной в локальной области, в которой она используется?» Вообще ничего. Никогда не нужно использовать global , когда вы просто хотите использовать глобальную переменную. Скорее, это необходимо только при попытке присвоить глобальной переменной внутри функции. Действительно, использование global in update_dict может сбить с толку , потому что люди, читающие его, будут ожидать, что вы измените эту глобальную переменную (присвоите или переназначите ее)

3. @juanpa.arrivillaga 1 — Это присвоение ключа объекту, который не был объявлен как dict . Откуда класс знает, что он должен использовать глобальную переменную?

4. Ваш класс никогда не использует глобальную переменную . В любом случае вам никогда не нужно объявлять переменные как глобальные, просто чтобы использовать их . Пожалуйста, прочтите, что я написал. Возможно some_dict[key] = value , вас смущает, это не простое назначение, например x = y , скорее, это «назначение элемента», в основном синтаксический сахар для some_dict.__setitem__(key, value) Вам нужно использовать global some_var оператор только тогда, когда some_var он назначен в некоторой неглобальной области

5. «Это присвоение ключа объекту, который не был объявлен как dict». Это не имеет никакого смысла. Я думаю, у вас фундаментальное недоразумение, python не имеет объявлений переменных, а переменные не имеют типов в Python, это динамически типизированный язык. Все update_dict , что происходит, происходит во время выполнения, и оно разрешается во время выполнения при вызове функции.

Ответ №1:

global varname требуется только при назначении глобальной переменной, потому что в противном случае предполагается, что создается новая локальная переменная. Во всех остальных случаях Python уже знает, что она должна быть используемой глобальной, и двусмысленности нет.

В вашем примере кода вы устанавливаете пару ключ / значение для глобального dict, а не присваиваете глобальной переменной:

 def update_dict(key):
    my_dict[key] = ...
 

Следующий случай, когда вам нужно global изменить глобальную b:

 b = 0
def a():
    global b
    b = 2
print(b)
a()
print(b)
 

Вывод:

 0
2
 

Комментарии:

1. функция класса присваивается глобальной переменной. Однако нет global varname . Кроме того, вы НЕ МОЖЕТЕ добавить новый ключ к a new local object , который не был объявлен как dict . Я удивлен, что скрипты работают, но я подозреваю, что результаты могут быть неверными.

2. @aerijman что? Нигде вы не присваиваете глобальной переменной в методах вашего класса.

3. mydict[key] = ... не является присвоением mydict . Это синтаксический сахар для mydict.__setitem__(key, ...) , и его нет NameError до тех пор, пока mydict он определен в содержащей области перед update_dict вызовом . В частности, mydict не нужно определять в то время update_dict , когда определяется само определение.

4. @chepner 1- Спасибо! Мне трудно это понять. Является ли хорошей практикой объявлять функции, которые будут использовать переменные, которые будут объявлены в будущем, вместо того, чтобы передавать их функции в качестве аргументов?

5. Не совсем, нет. передавайте значения в качестве аргументов, когда это возможно.