Приватные переменные в функции Python

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

Код программы:

import string
import random


class Cipher:
    __encryption_key = ""
    __user_data = ""

    @staticmethod
    def add_string(user_data) -> None:
        Cipher.__user_data = user_data

    @staticmethod
    def clear_user_data():
        del Cipher.__user_data

    @staticmethod
    def generate_encryption_key(length_key=32) -> str:
        Cipher.__encryption_key = "".join("".join(random.choices(string.ascii_letters, k=length_key)))
        return Cipher.__encryption_key

    @staticmethod
    def clear_encryption_key():
        del Cipher.__encryption_key

    @staticmethod
    def encrypt_user_string() -> str:
        encrypt_user_string = "".join(chr(ord(p) ^ ord(k)) for p, k in zip(Cipher.__user_data,
                                                                           Cipher.__encryption_key))
        return encrypt_user_string

    @staticmethod
    def decrypt_user_string(encryption_data: str, encryption_key: str) -> str:
        decrypt_user_string = "".join(chr(ord(c) ^ ord(k)) for c, k in zip(encryption_data, encryption_key))
        return decrypt_user_string

Сейчас вот так:

@staticmethod
    def decrypt_user_string(encryption_data: str, encryption_key: str) -> str:
        decrypt_user_string = "".join(chr(ord(c) ^ ord(k)) for c, k in zip(encryption_data, encryption_key))
        return decrypt_user_string

Требуется что-то типа такого:

 @staticmethod
    def decrypt_user_string(encryption_data=encrypt_user_string(), encryption_key: Cipher.__encryption_key) -> str:
        decrypt_user_string = "".join(chr(ord(c) ^ ord(k)) for c, k in zip(encryption_data, encryption_key))

Как это сделать? Код для проверки работы программы:

from vernam_cipher import Cipher


if __name__ == "__main__":
    user_data = "Привет, мир!"

    cipher = Cipher()
    encryption_key = cipher.generate_encryption_key()
    print(f"Ключ шифрования данных: {encryption_key}")

    cipher.add_string(user_data)

    print(f"Данные: {cipher._Cipher__user_data}")

    encrypt_user_string = cipher.encrypt_user_string()
    print(f"Зашифрованные данные: {encrypt_user_string}")
    decrypt_user_string = cipher.decrypt_user_string(encrypt_user_string, encryption_key)
    print(f"Расшифрованные данные: {decrypt_user_string}")

        return decrypt_user_string

Ответы (1 шт):

Автор решения: Amgarak

Ваш код кажется несколько странным.

Если у вас создаётся объект класса, то логично использовать методы, привязанные к объекту (нестатические), и сделать код более объектно-ориентированным.

Я бы посоветовал вам: избавится от статических методов, сделать приватные атрибуты более доступными через Property\Дескрипторы и работать с объектом класса, а не через статические вызовы. Или вообще всё решить на уровне модуля, не создавая класс вообще.


К тому же, у вас ключ всегда имеет фиксированную длину в 32 символа (само по себе это не проблема, но..). И вы не проверяете, что данные короче или длиннее ключа. Отсюда проблема - если строка длиннее 32 символов, лишние символы просто не будут зашифрованы. Как по мне, это нелогично.


Через класс:

import string
import random

class Cipher:
    def __init__(self, user_data=""):
        self.__encryption_key = ""
        self.__user_data = user_data
        self.__encrypted_data = ""

    @property
    def user_data(self):
        if not self.__user_data:
            return "Данных нет"
        return self.__user_data

    @user_data.setter
    def user_data(self, value):
        self.__user_data = value

    @property
    def encryption_key(self):
        if not self.__encryption_key:
            return "Ключ шифрования не был сгенерирован."
        return self.__encryption_key

    @property
    def encrypted_data(self):
        if not self.__encrypted_data:
            return "Зашифрованных данных нет."
        return self.__encrypted_data

    def generate_encryption_key(self, length_key=32) -> str:
        self.__encryption_key = "".join(random.choices(string.ascii_letters, k=length_key))
        return self.__encryption_key

    def encrypt_user_string(self) -> str:
        if not self.__encryption_key:
            raise ValueError("Ключ шифрования не был сгенерирован.")
        if not self.__user_data:
            raise ValueError("Нет данных для шифрования.")

        extended_key = (self.__encryption_key * (len(self.__user_data) // len(self.__encryption_key) + 1))[:len(self.__user_data)]
        
        self.__encrypted_data = "".join(chr(ord(p) ^ ord(k)) for p, k in zip(self.__user_data, extended_key))
        return self.__encrypted_data

    def decrypt_user_string(self) -> str:
        if not self.__encrypted_data:
            raise ValueError("Нет данных для расшифровки.")
        if not self.__encryption_key:
            raise ValueError("Ключ шифрования не был сгенерирован.")
        
        extended_key = (self.__encryption_key * (len(self.__encrypted_data) // len(self.__encryption_key) + 1))[:len(self.__encrypted_data)]
        
        return "".join(chr(ord(c) ^ ord(k)) for c, k in zip(self.__encrypted_data, extended_key))

if __name__ == "__main__":

    user_data = "Привет, мир! Длинная строка для шифрования. Длинная строка для шифрования. Длинная строка для шифрования. Длинна > 32"

    cipher = Cipher()
    print(cipher.user_data)
    print(cipher.encryption_key)
    print(cipher.encrypted_data)
    
    cipher.user_data = user_data
    print(f"Данные: {cipher.user_data}")

    encryption_key = cipher.generate_encryption_key()
    print(f"Ключ шифрования данных: {encryption_key}")
    print(f"Ключ: {cipher.encryption_key}")
    
    encrypted_data = cipher.encrypt_user_string()
    print(f"Зашифрованные данные: {encrypted_data}")
    print(f"Зашифрованные: {cipher.encrypted_data}")
    
    decrypted_data = cipher.decrypt_user_string()
    print(f"Расшифрованные данные: {decrypted_data}")

Вывод:

Данных нет
Ключ шифрования не был сгенерирован.
Зашифрованных данных нет.
Данные: Привет, мир! Длинная строка для шифрования. Длинная строка для шифрования. Длинная строка для шифрования. Длинна > 32
Ключ шифрования данных: CTLhjjBALdqkMaqBOEpUqmsPVpzfJrUX
Ключ: CTLhjjBALdqkMaqBOEpUqmsPVpzfJrUX
Зашифрованные данные: ќДѴњџШnaѰќбJmѵъѺѲѸрКQЬбАѨъъFѾщКxЋѬЈШєјѲѼѴЫ_KљњщѿѲѵпuаЯгѮѬрZђѱнuАѻАЌіјњѿѹЃJQѿѶљьѿѿЊPДгЭэѪѦPюѝЅRНѠЇДѲњњїѺЎbDѥѐѵќьѲo{PfC
Зашифрованные: ќДѴњџШnaѰќбJmѵъѺѲѸрКQЬбАѨъъFѾщКxЋѬЈШєјѲѼѴЫ_KљњщѿѲѵпuаЯгѮѬрZђѱнuАѻАЌіјњѿѹЃJQѿѶљьѿѿЊPДгЭэѪѦPюѝЅRНѠЇДѲњњїѺЎbDѥѐѵќьѲo{PfC
Расшифрованные данные: Привет, мир! Длинная строка для шифрования. Длинная строка для шифрования. Длинная строка для шифрования. Длинна > 32

На уровне модуля:

import string
import random


def generate_encryption_key(length_key=32) -> str:
    return "".join(random.choices(string.ascii_letters, k=length_key))


def encrypt_string(user_data: str, encryption_key: str) -> str:
    if not encryption_key:
        raise ValueError("Ключ шифрования не был передан.")
    if not user_data:
        raise ValueError("Нет данных для шифрования.")
    
    extended_key = (encryption_key * (len(user_data) // len(encryption_key) + 1))[:len(user_data)]
    
    return "".join(chr(ord(p) ^ ord(k)) for p, k in zip(user_data, extended_key))


def decrypt_string(encrypted_data: str, encryption_key: str) -> str:
    if not encrypted_data:
        raise ValueError("Нет данных для расшифровки.")
    if not encryption_key:
        raise ValueError("Ключ шифрования не был передан.")
    
    extended_key = (encryption_key * (len(encrypted_data) // len(encryption_key) + 1))[:len(encrypted_data)]
    
    return "".join(chr(ord(c) ^ ord(k)) for c, k in zip(encrypted_data, extended_key))


if __name__ == "__main__":
    user_data = "Привет, мир! Длинная строка для шифрования. Длинная строка для шифрования. Длинная строка для шифрования. Длинна > 32"
    
    encryption_key = generate_encryption_key()
    print(f"Ключ шифрования данных: {encryption_key}")
    
    encrypted_data = encrypt_string(user_data, encryption_key)
    print(f"Зашифрованные данные: {encrypted_data}")
    
    decrypted_data = decrypt_string(encrypted_data, encryption_key)
    print(f"Расшифрованные данные: {decrypted_data}")

Вывод:

Ключ шифрования данных: VxWnsvSsPzbPhxcIZytTQXTKAeMqgzll
Зашифрованные данные: щиѯќцдSѬтТqHѬјѱѧффЛqЙЖЋѿџѽQѓсУLОрГЮэфѣюѨеLpѼућѴѧщлtАКДѵѻѕmхќеLФѮмЗѐсцѮыПTBфѓрўѴѪжTЕГИѪѱѱEѹъШZФєВиѩќуыѫм~ZѶѫѐхўѹzGTgc
Расшифрованные данные: Привет, мир! Длинная строка для шифрования. Длинная строка для шифрования. Длинная строка для шифрования. Длинна > 32

Всё же если хочется сделать через класс, но при этом создание объекта не предполагается, то вместо @staticmethod лучше использовать @classmethod(так мы ЯВНО сохраним доступ к атрибутам-класса), а доступ к приватным атрибутам получать через геттеры и сеттеры:

import string
import random


class Cipher:
    __encryption_key = ""
    __encrypted_data = ""
    
    def __new__(cls):
        raise TypeError("Как вариант, можно запретить создание экземпляра класса Cipher")

    @classmethod
    def generate_encryption_key(cls, length_key=32) -> str:
        cls.__encryption_key = "".join(random.choices(string.ascii_letters, k=length_key))
        return cls.__encryption_key

    @classmethod
    def set_encrypted_data(cls, data: str):
        cls.__encrypted_data = data

    @classmethod
    def get_encryption_key(cls) -> str:
        return cls.__encryption_key

    @classmethod
    def get_encrypted_data(cls) -> str:
        return cls.__encrypted_data

    @classmethod
    def encrypt_string(cls, user_data: str) -> str:
        if not cls.__encryption_key:
            raise ValueError("Ключ шифрования не был сгенерирован.")
        if not user_data:
            raise ValueError("Нет данных для шифрования.")

        extended_key = (cls.__encryption_key * (len(user_data) // len(cls.__encryption_key) + 1))[:len(user_data)]

        encrypted_data = "".join(chr(ord(p) ^ ord(k)) for p, k in zip(user_data, extended_key))
        cls.set_encrypted_data(encrypted_data)  # Сохраняем зашифрованные данные
        return encrypted_data

    @classmethod
    def decrypt_string(cls, encryption_data: str = None, encryption_key: str = None) -> str:
        if encryption_data is None:
            encryption_data = cls.get_encrypted_data()  # Используем хранящиеся данные
        if encryption_key is None:
            encryption_key = cls.get_encryption_key()  # Используем хранящийся ключ

        if not encryption_data:
            raise ValueError("Нет данных для расшифровки.")
        if not encryption_key:
            raise ValueError("Ключ шифрования не был передан.")

        extended_key = (encryption_key * (len(encryption_data) // len(encryption_key) + 1))[:len(encryption_data)]

        return "".join(chr(ord(c) ^ ord(k)) for c, k in zip(encryption_data, extended_key))


if __name__ == "__main__":
    user_data = "Привет, мир! Длинная строка для шифрования."

    encryption_key = Cipher.generate_encryption_key()
    print(f"Ключ шифрования данных: {encryption_key}")

    encrypted_data = Cipher.encrypt_string(user_data)
    print(f"Зашифрованные данные: {encrypted_data}")

    decrypted_data = Cipher.decrypt_string()  
    print(f"Расшифрованные данные: {decrypted_data}")
→ Ссылка