Проверка на членство внутри вложенного dict - PullRequest
1 голос
/ 25 мая 2010

Это дополнительные вопросы к этому:

Python DictReader - пропуск строк с пропущенными столбцами?

Оказывается, я был глуп, и использовал неправильное поле ID.

Я использую Python 3.x, кстати.

У меня есть команда сотрудников, проиндексированная строкой "directory_id". Каждое значение представляет собой вложенный диктант с атрибутами сотрудника (номер телефона, фамилия и т. Д.). Одним из этих значений является вторичный идентификатор, скажем «internal_id», а другое - их менеджер, назовите его «manager_internal_id». Поле «internal_id» не является обязательным, и не у каждого сотрудника есть.

{'6443410501': {'manager_internal_id': '989634', 'givenName': 'Mary', 'phoneNumber': '+65 3434 3434', 'sn': 'Jones', 'internal_id': '434214'}
'8117062158': {'manager_internal_id': '180682', 'givenName': 'John', 'phoneNumber': '+65 3434 3434', 'sn': 'Ashmore', 'internal_id': ''}
'9227629067': {'manager_internal_id': '347394', 'givenName': 'Wright', 'phoneNumber': '+65 3434 3434', 'sn': 'Earl', 'internal_id': '257839'}
'1724696976': {'manager_internal_id': '907239', 'givenName': 'Jane', 'phoneNumber': '+65 3434 3434', 'sn': 'Bronte', 'internal_id': '629067'}

}

(Я немного упростил поля, чтобы было легче читать, а также по соображениям конфиденциальности / соответствия).

Проблема здесь в том, что мы индексируем (ключим) каждого сотрудника по его directory_id, но когда мы ищем их менеджера, нам нужно найти менеджеров по их «внутреннему_иде».

Раньше, когда наш dict использовал internal_id в качестве ключа, employee.keys () представлял собой список internal_id, и я использовал проверку членства по этому вопросу. Теперь последняя часть моего оператора if не будет работать, так как internal_ids является частью значений dict, а не самого ключа.

def lookup_supervisor(manager_internal_id, employees):
    if manager_internal_id is not None and manager_internal_id != "" and manager_internal_id in employees.keys():
        return (employees[manager_internal_id]['mail'], employees[manager_internal_id]['givenName'], employees[manager_internal_id]['sn'])
    else:
        return ('Supervisor Not Found', 'Supervisor Not Found', 'Supervisor Not Found')

Итак, первый вопрос: как мне исправить оператор if, чтобы проверить, присутствует ли manager_internal_id в списке dict internal_ids?

Я пытался заменить employee.keys () на employee.values ​​(), но это не сработало. Кроме того, я надеюсь на что-то более эффективное, не уверен, есть ли способ получить подмножество значений, в частности, все записи для сотрудников [directory_id] ['internal_id'].

Надеюсь, есть какой-то Pythonic способ сделать это, не используя огромную кучу вложенных циклов for / if.

Мой второй вопрос: как мне тогда правильно вернуть необходимые атрибуты сотрудника (почта, имя, фамилия и т. Д.). Мой цикл for выполняет итерацию по каждому сотруднику и вызывает lookup_supervisor. Я чувствую себя немного глупо / тупо здесь.

def tidy_data(employees):
    for directory_id, data in employees.items():
        # We really shouldnt' be passing employees back and forth like this - hmm, classes?
        data['SupervisorEmail'], data['SupervisorFirstName'], data['SupervisorSurname'] = lookup_supervisor(data['manager_internal_id'], employees)

Должен ли я перепроектировать мою структуру данных? Или есть другой способ?

РЕДАКТИРОВАТЬ: Я немного подправил код, см. Ниже:

class Employees:

    def import_gd_dump(self, input_file="test.csv"):
        gd_extract = csv.DictReader(open(input_file), dialect='excel')
        self.employees = {row['directory_id']:row for row in gd_extract}

    def write_gd_formatted(self, output_file="gd_formatted.csv"):
        gd_output_fieldnames = ('internal_id', 'mail', 'givenName', 'sn', 'dbcostcenter', 'directory_id', 'manager_internal_id', 'PHFull', 'PHFull_message', 'SupervisorEmail', 'SupervisorFirstName', 'SupervisorSurname')
        try:
            gd_formatted = csv.DictWriter(open(output_file, 'w', newline=''), fieldnames=gd_output_fieldnames, extrasaction='ignore', dialect='excel')
        except IOError:
            print('Unable to open file, IO error (Is it locked?)')
            sys.exit(1)

        headers = {n:n for n in gd_output_fieldnames}
        gd_formatted.writerow(headers)
        for internal_id, data in self.employees.items():
            gd_formatted.writerow(data)

    def tidy_data(self):
        for directory_id, data in self.employees.items():
            data['PHFull'], data['PHFull_message'] = self.clean_phone_number(data['telephoneNumber'])
            data['SupervisorEmail'], data['SupervisorFirstName'], data['SupervisorSurname'] = self.lookup_supervisor(data['manager_internal_id'])

    def clean_phone_number(self, original_telephone_number):
        standard_format = re.compile(r'^\+(?P<intl_prefix>\d{2})\((?P<area_code>\d)\)(?P<local_first_half>\d{4})-(?P<local_second_half>\d{4})')
        extra_zero = re.compile(r'^\+(?P<intl_prefix>\d{2})\(0(?P<area_code>\d)\)(?P<local_first_half>\d{4})-(?P<local_second_half>\d{4})')
        missing_hyphen = re.compile(r'^\+(?P<intl_prefix>\d{2})\(0(?P<area_code>\d)\)(?P<local_first_half>\d{4})(?P<local_second_half>\d{4})')
        if standard_format.search(original_telephone_number):
            result = standard_format.search(original_telephone_number)
            return '0' + result.group('area_code') + result.group('local_first_half') + result.group('local_second_half'), ''
        elif extra_zero.search(original_telephone_number):
            result = extra_zero.search(original_telephone_number)
            return '0' + result.group('area_code') + result.group('local_first_half') + result.group('local_second_half'), 'Extra zero in area code - ask user to remediate. '
        elif missing_hyphen.search(original_telephone_number):
            result = missing_hyphen.search(original_telephone_number)
            return '0' + result.group('area_code') + result.group('local_first_half') + result.group('local_second_half'), 'Missing hyphen in local component - ask user to remediate. '
        else:
            return '', "Number didn't match format. Original text is: " + original_telephone_number    

    def lookup_supervisor(self, manager_internal_id):
        if manager_internal_id is not None and manager_internal_id != "":# and manager_internal_id in self.employees.values():
            return (employees[manager_internal_id]['mail'], employees[manager_internal_id]['givenName'], employees[manager_internal_id]['sn'])
        else:
            return ('Supervisor Not Found', 'Supervisor Not Found', 'Supervisor Not Found')

if __name__ == '__main__':
    our_employees = Employees()
    our_employees.import_gd_dump('test.csv')
    our_employees.tidy_data()
    our_employees.write_gd_formatted()

Я думаю (1). Я ищу лучший способ структурировать / хранить сотрудников / сотрудников, и (2) у меня, в частности, проблемы с lookup_supervisor (). \

Должен ли я создавать класс Employee и вкладывать его в Employees?

И должен ли я даже делать то, что я делаю с tidy_data () и вызывать clean_phone_number () и lookup_supervisor () в цикле for для элементов dict? Urgh. смущенно .

Ответы [ 2 ]

2 голосов
/ 25 мая 2010

Вам, вероятно, потребуется выполнить итерацию, чтобы получить данные. Я предполагаю, что вам не нужен дополнительный диктат, который может устареть, поэтому не стоит пытаться хранить все, что связано с внутренними идентификаторами.

Примерьте размер:

def lookup_supervisor(manager_internal_id, employees):
    if manager_internal_id is not None and manager_internal_id != "":
        manager_dir_ids = [dir_id for dir_id in employees if employees[dir_id].get('internal_id') == manager_internal_id]
        assert(len(manager_dir_ids) <= 1)
        if len(manager_dir_ids) == 1:
            return manager_dir_ids[0]
    return None

def tidy_data(employees):
    for emp_data in employees.values():
        manager_dir_id = lookup_supervisor(emp_data.get('manager_internal_id'), employees)
        for (field, sup_key) in [('Email', 'mail'), ('FirstName', 'givenName'), ('Surname', 'sn')]:
            emp_data['Supervisor'+field] = (employees[manager_dir_id][sup_key] if manager_dir_id is not None else 'Supervisor Not Found')

И вы определенно правы, что класс - это ответ на прохождение employees. На самом деле, я бы рекомендовал не хранить ключи 'Супервизор' в диктовке сотрудника и вместо этого предлагать обновлять диктор супервизора всякий раз, когда вам это нужно, возможно, с помощью метода get_supervisor_data.

Ваша новая версия OO выглядит разумно, за исключением изменений, о которых я уже упоминал, и некоторых настроек clean_phone_number.

def clean_phone_number(self, original_telephone_number):
    phone_re = re.compile(r'^\+(?P<intl_prefix>\d{2})\((?P<extra_zero>0?)(?P<area_code>\d)\)(?P<local_first_half>\d{4})(?P<hyph>-?)(?P<local_second_half>\d{4})')
    result = phone_re.search(original_telephone_number)
    if result is None:
        return '', "Number didn't match format. Original text is: " + original_telephone_number
    msg = ''
    if result.group('extra_zero'):
        msg += 'Extra zero in area code - ask user to remediate. '
    if result.group('hyph'):    # Note: can have both errors at once
        msg += 'Missing hyphen in local component - ask user to remediate. '
    return '0' + result.group('area_code') + result.group('local_first_half') + result.group('local_second_half'), msg

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

1 голос
/ 25 мая 2010

Мои навыки в питоне плохие, поэтому я слишком невежественен, чтобы писать то, что я имею в виду, в любое разумное время.Но я знаю, как выполнить разложение OO.

Почему класс Employees выполняет всю работу?Ваш монолитный класс Employees выполняет несколько типов действий:

  • Чтение и запись данных из файла - также как сериализация
  • Управление и доступ к данным отдельных сотрудников
  • Управление отношениями между бывшими сотрудниками.

Я предлагаю создать класс для обработки каждой из перечисленных групп задач.

Определить класс Employee для отслеживания данных сотрудника и обработки поляобработка / приведение в порядок задач.

Используйте класс Employees в качестве контейнера для объектов сотрудников.Он может обрабатывать такие задачи, как отслеживание руководителя сотрудника.

Определите виртуальный базовый класс EmployeeLoader для определения интерфейса (загрузка, хранение, ??).Затем реализуйте подкласс для сериализации файла CSV.(Виртуальный базовый класс является необязательным - я не уверен, как Python обрабатывает виртуальные классы, так что это может даже не иметь смысла.)

Итак:

  • создать экземплярEmployeeCSVLoader с именем файла для работы.
  • После этого загрузчик может создать объект Employees и проанализировать файл.
  • По мере чтения каждой записи новый объект Employee будетсоздан и сохранен в объекте Employees.
  • Теперь попросите объект Employees заполнить ссылки супервизора.
  • Переберите коллекцию сотрудников объекта Employees и попросите каждого привести себя в порядок.
  • Наконец, пусть объект сериализации обрабатывает обновление файла данных.

Почему этот проект стоит усилий?

Это облегчает понимание.Меньшие, ориентированные на задачи объекты проще создавать чистые, согласованные API для.

Если вы обнаружите, что вам нужен формат сериализации XML, добавить новый формат становится тривиально.Подкласс вашего класса виртуального загрузчика для обработки / анализа XML.Теперь вы можете легко перемещаться между форматами CSV и XML.

В общем, используйте объекты для упрощения и структурирования ваших данных.Разделите общие данные и поведение на отдельные классы.Держите каждый класс тесно сосредоточенным на одном типе способностей.Если ваш класс представляет собой коллекцию, аксессор, фабрику, кухонную раковину, API никогда не сможет быть использован: он будет слишком большим и загруженным различными группами методов.Но если ваши занятия будут посвящены теме, их будет легко тестировать, поддерживать, использовать, повторно использовать и расширять.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...