توليد UUID v4 في Python — uuid.uuid4()

·Backend Developer·مراجعة بواسطةDmitri Volkov·نُشر

استخدم مولّد UUID v4 المجاني مباشرةً في متصفحك — لا حاجة للتثبيت.

جرّب مولّد UUID v4 أونلاين ←

في كل مرة أحتاج فيها إلى معرّف مقاوم للتصادم لصف في قاعدة بيانات، أو تتبّع API، أو رمز جلسة، يكون الحل هو توليد UUID v4 في Python — سطر واحد، بدون أي تبعيات: uuid.uuid4(). تستخدم وحدة uuid المدمجة في Python os.urandom() لتوليد عشوائية آمنة تشفيريًا. للحصول على UUID سريع دون كتابة كود، يعمل مولّد UUID v4 الإلكتروني فورًا. يغطي هذا الدليل خصائص كائن UUID، التوليد الجماعي، التسلسل إلى JSON، التخزين في قواعد البيانات، التحقق من الصحة، uuid-utils (بديل مدعوم بـ Rust أسرع بـ~10×)، والأخطاء الأربعة الأكثر شيوعًا — كل ذلك مع Python 3.8+. سواء كنت تبني خدمة صغيرة أو نظامًا موزّعًا على نطاق واسع، فإن فهم كيفية اختيار التمثيل الصحيح لـ UUID — سلسلة نصية أو hex أو bytes — يُجنّبك أخطاء التحويل الصامتة ويضمن التوافقية مع جميع طبقات المكدّس. يُعدّ UUID الإصدار الرابع خيارًا مثاليًا عند الحاجة إلى معرّفات آمنة في البيئات الموزعة، إذ يضمن التفرد العالمي دون الاعتماد على تنسيق مركزي أو قاعدة بيانات مشتركة.

النقاط الرئيسية
  • uuid.uuid4() مدمجة في stdlib الخاصة بـ Python — كل ما تحتاجه هو import uuid، دون أي pip install.
  • القيمة المُعادة هي كائن uuid.UUID وليست سلسلة نصية — استخدم str() أو .hex أو .bytes لاختيار التمثيل المناسب لطبقة التخزين لديك.
  • يستخدم UUID v4 أعداد عشوائية من 122 بت مأخوذة من os.urandom() — آمن تشفيريًا، لا يكشف عنوان MAC ولا طابع زمني.
  • للخدمات عالية الإنتاجية، يعدّ pip install uuid-utils بديلًا متوافقًا أسرع بـ~10× مدعومًا بـ Rust.
  • لا تمرّر uuid.uuid4 (بدون أقواس) كقيمة افتراضية مباشرةً في dataclass أو نموذج Pydantic — سيتشارك جميع النسخ UUID واحدًا فقط.

ما هو UUID v4؟

UUID (المعرّف الفريد العالمي) هو تسمية من 128 بت مُنسَّقة على شكل 32 رقمًا سداسيًا عشريًا مقسّمة إلى خمس مجموعات بشَرَطات: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. الإصدار 4 هو المتغيّر الأكثر استخدامًا: 122 من تلك الـ 128 بت تُولَّد عشوائيًا، و6 بتات المتبقية تُشفّر الإصدار (4) والمتغيّر (RFC 4122). لا يوجد طابع زمني ولا معرّف مضيف — المعرّف غامض تمامًا وآمن للخصوصية. احتمال تصادم اثنين من v4 UUIDs المُولَّدَين بشكل مستقل ضئيل جدًا لدرجة أنه لأغراض عملية لا يحدث أبدًا، حتى في الأنظمة الموزعة التي تولّد ملايين المعرّفات في الثانية. على عكس معرّفات العداد التسلسلي، لا تتطلب UUIDs v4 تنسيقًا مركزيًا بين العقد أو قاعدة البيانات، مما يجعلها مثالية للبيئات الموزعة ومتعددة الخدمات حيث قد تولّد أجهزة مختلفة معرّفات في نفس اللحظة. هذه اللامركزية الكاملة تعني أن توليد UUID v4 صالح لا يستلزم أي اتصال بالشبكة أو خادم تنسيق مركزي — كل ما تحتاجه هو مولّد أعداد عشوائية آمن تشفيريًا، وهو متاح افتراضيًا على جميع المنصات الحديثة التي تدعم Python سواء على الخادم أو داخل حاويات Docker أو في وظائف Lambda الخالية من الخادم.

After · Python
Before · Python
event_id = str(uuid.uuid4())  # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
event_id = "evt-" + str(random.randint(100000, 999999))  # fragile, not unique

uuid.uuid4() — الطريقة القياسية لتوليد UUID v4 في Python

وحدة uuid جزء من المكتبة القياسية في Python. يُعيد استدعاء uuid.uuid4() كائن uuid.UUID يمتلك مجموعة كاملة من الخصائص لتمثيلات مختلفة. يُنتج التحويل إلى سلسلة نصية باستخدام str() التنسيق القياسي بالشَرَطات الذي تتوقعه APIs وقواعد البيانات ورؤوس HTTP.

Python 3.8+ — مثال مبسّط
import uuid

# توليد UUID v4
request_id = uuid.uuid4()

print(request_id)           # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
print(type(request_id))     # <class 'uuid.UUID'>
print(request_id.version)   # 4

# التحويل إلى سلسلة نصية لـ JSON / رؤوس HTTP
print(str(request_id))      # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"
print(request_id.hex)       # "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" (بدون شَرَطات)
print(request_id.bytes)     # b';...' (16 بايت خام)

من الأنماط الشائعة في بيئات الإنتاج إرفاق UUID بكل طلب API صادر لتمكين ربط السجلات عبر الخدمات. هذا النمط، المعروف بـ"Correlation ID" أو معرّف الارتباط، يُتيح لك تتبع دورة حياة الطلب الكاملة عبر خدمات متعددة في بنية الخدمات المصغّرة، مما يجعل استكشاف الأخطاء أسرع بكثير عند البحث في سجلات الإنتاج. إليك غلاف بسيط لجلسة requests يحقن UUID جديدًا في كل استدعاء:

Python 3.8+ — معرّف تتبع لكل طلب
import uuid
import requests

def call_api(endpoint: str, payload: dict) -> dict:
    trace_id = str(uuid.uuid4())
    headers = {
        "X-Request-ID": trace_id,
        "Content-Type": "application/json",
    }
    response = requests.post(endpoint, json=payload, headers=headers, timeout=10)
    response.raise_for_status()
    return {"trace_id": trace_id, "data": response.json()}

# result["trace_id"] lets you grep the exact request across all service logs
result = call_api("https://api.example.com/v1/orders", {"product_id": "prod_7x2k", "qty": 3})
print(result["trace_id"])  # e.g. "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"

عند توليد UUIDs بكميات كبيرة — مثلًا لملء دفعة من صفوف قاعدة البيانات مسبقًا — يُعدّ list comprehension أسلوبًا بديهيًا وسهل القراءة. وعند الحاجة إلى توليد قوائم ضخمة جدًا دون استهلاك كبير للذاكرة، يُفضَّل استخدام تعبير مولّد بدلًا من قائمة لتوليد كل UUID عند الطلب بدلًا من تخصيص جميع القيم في الذاكرة مسبقًا:

Python 3.8+ — التوليد الجماعي
import uuid

# توليد معرّفات مسبقًا لـ 1000 حدث قياس
event_ids = [str(uuid.uuid4()) for _ in range(1000)]
print(f"Generated {len(event_ids)} unique IDs")
print(event_ids[0])   # e.g. "a1c2e3f4-..."
print(event_ids[-1])  # قيمة مختلفة في كل مرة

هل تحتاج إلى UUID سريع دون تشغيل أي كود؟ استخدم مولّد UUID v4 الإلكتروني لنسخ قيمة جديدة بنقرة واحدة، أو لتوليد المئات دفعةً — مفيد لملء قواعد بيانات الاختبار أو ملفات البيانات التجريبية.

ملاحظة:تستدعي uuid.uuid4() داخليًا os.urandom(16)، ثم تضبط البتات 6–7 من البايت 8 على 10 (المتغيّر) والبتات 12–15 من البايت 6 على 0100 (الإصدار 4). الـ 122 بت المتبقية عشوائية. لهذا السبب لا يمكنك الوثوق بالإصدار إلا إذا حللت القيمة باستخدام uuid.UUID().

خصائص وتمثيلات كائن UUID

يُتيح كائن uuid.UUID تمثيلات متعددة لنفس القيمة ذات 128 بت. اختيار التمثيل الصحيح لطبقة التخزين يمنع الفساد الصامت للبيانات والبايتات المهدورة.

الخاصية / الطريقة
النوع
الوصف
uuid.UUID(hex=...)
UUID
تحليل UUID موجود من سلسلة hex، مع أو بدون شَرَطات.
.hex
str
سلسلة hex من 32 حرفًا بأحرف صغيرة بدون شَرَطات — تنسيق التخزين المضغوط.
.int
int
تمثيل عدد صحيح 128 بت للـ UUID — مفيد للعمليات الحسابية والترتيب.
.bytes
bytes
تمثيل ثنائي big-endian من 16 بايت — أكثر أحجام التخزين كفاءة.
.bytes_le
bytes
ثنائي little-endian من 16 بايت — يطابق ترتيب بايت Microsoft GUID.
.fields
tuple
صف سداسي من حقول UUID: (time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node).
.version
int | None
رقم إصدار UUID (من 1 إلى 5، أو None للـ UUIDs غير القياسية).
.variant
str
سلسلة متغيّر UUID — "specified in RFC 4122" للـ UUIDs القياسية.
str(uuid_obj)
str
سلسلة من 36 حرفًا بالتنسيق القياسي مع أربع شَرَطات: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
Python 3.8+ — جميع التمثيلات
import uuid

u = uuid.uuid4()

print(str(u))         # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"  (36 حرفًا)
print(u.hex)          # "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e"      (32 حرفًا، بدون شَرَطات)
print(u.bytes)        # b';Š...'                       (16 بايت، big-endian)
print(u.bytes_le)     # b'Š...'                       (16 بايت، little-endian)
print(u.int)          # 78823... (عدد صحيح 128 بت)
print(u.version)      # 4
print(u.variant)      # 'specified in RFC 4122'

# إعادة البناء: استرجاع UUID من سلسلة نصية
reconstructed = uuid.UUID("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e")
print(reconstructed == u)  # True (إذا كانت u تلك القيمة)

مع PostgreSQL عبر psycopg2 أو asyncpg، مرّر كائن UUID مباشرةً — يتولى المشغّل التحويل إلى نوع العمود uuid الأصلي. أما مع SQLite، استخدم str(u) (TEXT) أو u.bytes (BLOB، 16 بايت مقابل 36 للسلسلة النصية). لكفاءة التخزين على نطاق واسع، .bytes أصغر بنسبة 55% من السلسلة القياسية. تذكّر أن استخدام .bytes_le ضروري فقط حين تتعامل مع أنظمة Microsoft مثل SQL Server أو COM التي تستخدم ترتيب little-endian للبايت؛ في جميع الحالات الأخرى التزم بـ .bytes (big-endian) لتجنب إعادة ترتيب البتات عند الاسترجاع.

التحقق من صحة وتحليل سلاسل UUID v4 في Python

في كل مرة يصل فيها UUID من مدخلات المستخدم أو معامل مسار URL أو API خارجي، يجب التحقق منه قبل استخدامه كمفتاح في قاعدة البيانات. الأسلوب الاصطلاحي هو محاولة الإنشاء باستخدام uuid.UUID() واصطياد ValueError. يمكنك أيضًا التأكد من أن القيمة الواردة هي تحديدًا الإصدار 4 بالتحقق من .version. هذا مهم بشكل خاص في نقاط نهاية REST APIs التي تستقبل معرّفات الموارد من العملاء — فالتحقق المبكر يمنع استعلامات قاعدة البيانات غير الضرورية التي ستفشل بأي حال عند إدخال قيمة مشوّهة، كما يحمي من هجمات الحقن التي تحاول استغلال محللات UUID الضعيفة.

Python 3.8+ — دالة مساعدة للتحقق
import uuid

def parse_uuid4(raw: str) -> uuid.UUID:
    """
    تحليل والتحقق من صحة سلسلة UUID v4.
    ترفع ValueError للتنسيق غير الصالح أو الإصدار الخاطئ.
    """
    try:
        u = uuid.UUID(raw)
    except ValueError as exc:
        raise ValueError(f"Invalid UUID format: {raw!r}") from exc

    if u.version != 4:
        raise ValueError(f"Expected UUID v4, got v{u.version}: {raw!r}")

    return u

# الاستخدام في معالج مسار FastAPI / Flask
def get_order(order_id: str):
    try:
        uid = parse_uuid4(order_id)
    except ValueError as exc:
        return {"error": str(exc)}, 400

    # آمن لاستخدام uid في استعلام قاعدة البيانات الآن
    return {"order_id": str(uid), "status": "processing"}
ملاحظة:تقبل uuid.UUID() سلاسل بشَرَطات أو بدونها، وتقبل أيضًا البادئة urn:uuid:. لذا فإن "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" (بدون شَرَطات) و "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" كلاهما يُحلَّلان إلى نفس الكائن. هذه المرونة تعني أنه يمكنك قبول قيم UUID من مصادر مختلفة دون الحاجة إلى تطبيع التنسيق مسبقًا، مما يُبسّط معالجة المدخلات في نقاط نهاية API. كذلك يتجاهل المحلّل حالة الأحرف تمامًا، فسواء وردت الأحرف السداسية العشرية بالكبيرة أو الصغيرة، يُعيد المحلّل نفس كائن UUID مما يوفّر عليك خطوة تطبيع إضافية عند التعامل مع بيانات قادمة من مصادر متعددة ذات اصطلاحات تنسيق مختلفة.

UUID v4 في حمولات JSON واستجابات API

لا يمتلك معيار JSON نوع UUID — إذ يكون UUID في JSON دائمًا سلسلة نصية. هذا يعني أنك يجب أن تحوّل كائن uuid.UUID إلى سلسلة قبل تمريره إلى json.dumps(). الأسلوب الأنظف هو إنشاء فئة فرعية من JSONEncoder حتى لا تضطر إلى نثر استدعاءات str() في جميع أنحاء الكود.

Python 3.8+ — JSONEncoder مخصص لـ UUID
import json
import uuid
from datetime import datetime

class ApiEncoder(json.JSONEncoder):
    """تسلسل كائنات UUID وdatetime في استجابات JSON."""
    def default(self, obj):
        if isinstance(obj, uuid.UUID):
            return str(obj)
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

# استجابة API واقعية تحتوي على UUIDs متداخلة
api_response = {
    "request_id": uuid.uuid4(),
    "created_at": datetime(2026, 3, 14, 9, 41, 0),
    "order": {
        "id": uuid.uuid4(),
        "customer_id": uuid.uuid4(),
        "items": [
            {"product_id": uuid.uuid4(), "sku": "NVX-9000", "qty": 2},
        ],
    },
}

print(json.dumps(api_response, indent=2, cls=ApiEncoder))
# {
#   "request_id": "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e",
#   "created_at": "2026-03-14T09:41:00",
#   "order": {
#     "id": "a1c2e3f4-...",
#     ...
#   }
# }

لاستدعاء تسلسل فردي، يكون خيار default= أبسط من إنشاء فئة فرعية:

Python 3.8+ — خيار default= (استخدام فردي)
import json, uuid

event_id = uuid.uuid4()
payload = {"event_id": event_id, "action": "checkout"}

# تمرير دالة قابلة للاستدعاء؛ تُستدعى فقط للأنواع التي لا يستطيع json التعامل معها
json_str = json.dumps(payload, default=str)
print(json_str)  # {"event_id": "3b1f8a9d-...", "action": "checkout"}

عند استقبال استجابة من API خارجي، حوّل سلاسل UUID إلى كائنات لتحصل على مجموعة الخصائص الكاملة وأمان الأنواع:

Python 3.8+ — تحليل UUID من استجابة API
import json
import uuid
import requests

def fetch_shipment(shipment_id: str) -> dict:
    """جلب شحنة وإعادتها مع حقول UUID محدّدة الأنواع."""
    response = requests.get(
        f"https://api.logistics.example.com/v2/shipments/{shipment_id}",
        headers={"Accept": "application/json"},
        timeout=10,
    )
    response.raise_for_status()
    data = response.json()

    # تحليل حقول UUID إلى كائنات uuid.UUID
    try:
        data["id"] = uuid.UUID(data["id"])
        data["carrier_id"] = uuid.UUID(data["carrier_id"])
    except (KeyError, ValueError) as exc:
        raise RuntimeError(f"Malformed shipment response: {exc}") from exc

    return data

لتحديث حقول UUID في ملف JSON على القرص — مثلًا تدوير معرّف الارتباط في ملف إعداد أو بيانات تجريبية — اقرأه وعدّله واكتبه مجددًا بشكل ذري:

Python 3.8+ — قراءة وتحديث وكتابة ملف JSON
import json, uuid

def rotate_correlation_id(path: str) -> str:
    """استبدال أو إضافة 'correlation_id' في ملف JSON. يُعيد UUID الجديد."""
    try:
        with open(path) as f:
            data = json.load(f)
    except FileNotFoundError:
        data = {}
    except json.JSONDecodeError as exc:
        raise ValueError(f"Invalid JSON in {path!r}: {exc}") from exc

    new_id = str(uuid.uuid4())
    data["correlation_id"] = new_id

    with open(path, "w") as f:
        json.dump(data, f, indent=2)

    return new_id

إذا كنت لا تريد تشغيل سكريبت في كل مرة تحتاج فيها إلى فحص UUID من استجابة API، الصقه مباشرةً في محلّل UUID — يُظهر لك الإصدار والمتغيّر وجميع الحقول دون أي كود.

توليد UUID v4 من سطر الأوامر باستخدام Python

لا تعرض وحدة uuid في Python أمر CLI مستقلًا مثل python -m json.tool، لكن سطرًا واحدًا يغطي نفس حالة الاستخدام. هذه الأوامر مفيدة في سكريبتات الشل، وخطوط CI، وفي أي وقت تحتاج فيه إلى معرّف مؤقت دون فتح REPL. يمكنك كذلك تضمين توليد UUID في خطوط الأنابيب الأتمتة، مثل إنشاء معرّفات فريدة للتجارب وحفظها في متغيّرات البيئة تلقائيًا دون الحاجة إلى كتابة سكريبت Python كامل.

bash
# Single UUID v4
python3 -c "import uuid; print(uuid.uuid4())"
# 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e

# No-dashes (hex) format — useful for filenames and env vars
python3 -c "import uuid; print(uuid.uuid4().hex)"
# 3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e

# Generate 5 UUIDs for a batch seed script
python3 -c "import uuid; [print(uuid.uuid4()) for _ in range(5)]"

# Use in a shell variable
DEPLOY_ID=$(python3 -c "import uuid; print(uuid.uuid4())")
echo "Deploying with ID: $DEPLOY_ID"
ملاحظة:على macOS ومعظم توزيعات Linux، تُنتج uuidgen (أداة مكتوبة بـ C) قيم UUID v4 وهي أسرع للسكريبتات النقية. استخدم السطر الواحد بـ Python حين تكون بالفعل في بيئة تعتمد على Python وتريد الاتساق مع طريقة توليد UUIDs في كود تطبيقك. تجدر الإشارة إلى أن بعض إصدارات uuidgen القديمة على أنظمة معيّنة قد تولّد UUID v1 (القائم على الطابع الزمني) بدلًا من v4 — بينما تضمن uuid.uuid4() في Python دائمًا إنتاج v4 بغض النظر عن نظام التشغيل أو إعداداته.

UUID v4 عالي الأداء مع uuid-utils

تُعدّ uuid.uuid4() في المكتبة القياسية سريعةً بما يكفي لمعظم التطبيقات — ففي ميكروثوانٍ لكل استدعاء تعالج آلاف المعرّفات في الثانية بسهولة. إذا كنت تولّد UUIDs في المسار الحرج لخدمة عالية الإنتاجية (إدراجات جماعية، قياس أحداث على نطاق واسع، أو توليد معرّفات الطلبات تحت حمل ثقيل)، فإن uuid-utils بديل متوافق مدعوم بـ Rust يُحقق في المعايير سرعةً تبلغ نحو 10 أضعاف المكتبة القياسية.

bash — install
pip install uuid-utils
Python 3.8+ — uuid-utils كبديل متوافق
# uuid_utils is a drop-in replacement for the stdlib uuid module
import uuid_utils as uuid

# نفس واجهة برمجة المكتبة القياسية
request_id = uuid.uuid4()
print(request_id)           # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
print(str(request_id))      # canonical string
print(request_id.hex)       # no-dashes hex
print(request_id.version)   # 4

# تدعم أيضًا v7 (مرتّب زمنيًا، مثالي لمفاتيح DB الأساسية)
time_ordered_id = uuid.uuid7()
print(time_ordered_id)      # starts with current-timestamp prefix
تحذير:يُعيد الوضع الافتراضي لـ uuid-utils نوع كائن UUID خاصًا به، وهو متوافق مع المكتبة القياسية في معظم الحالات. إذا احتجت إلى فحوصات isinstance(u, uuid.UUID) الصارمة من المكتبة القياسية، استخدم وضع التوافق: import uuid_utils.compat as uuid. وضع التوافق أبطأ قليلًا من الوضع الافتراضي لكنه لا يزال أسرع من المكتبة القياسية.

UUID v4 في Dataclasses ونماذج Pydantic

تدعم Python dataclasses ونماذج Pydantic حقول UUID بشكل أصلي. النمط الأساسي عند استخدام UUID كقيمة افتراضية مُولَّدة تلقائيًا هو تمرير مرجع الدالة وليس نتيجة استدعائها — وإلا ستتشارك جميع النسخ نفس UUID.

Python 3.10+ — dataclass مع قيمة UUID افتراضية
from dataclasses import dataclass, field
import uuid

@dataclass
class WorkerJob:
    job_id: uuid.UUID = field(default_factory=uuid.uuid4)
    worker_id: str = "worker-01"
    payload: dict = field(default_factory=dict)

job1 = WorkerJob(payload={"task": "resize_image", "src": "uploads/img_4932.png"})
job2 = WorkerJob(payload={"task": "send_email",   "to":  "ops@example.com"})

print(job1.job_id)  # فريد لكل نسخة
print(job2.job_id)  # مختلف عن job1.job_id
print(job1.job_id == job2.job_id)  # False
Python 3.10+ — نموذج Pydantic v2 مع UUID
from pydantic import BaseModel, Field
import uuid

class OrderEvent(BaseModel):
    event_id: uuid.UUID = Field(default_factory=uuid.uuid4)
    order_id: uuid.UUID
    status: str
    amount_cents: int

event = OrderEvent(
    order_id=uuid.UUID("a1c2e3f4-5b6d-4e7f-8c9d-0a1b2c3d4e5f"),
    status="payment_confirmed",
    amount_cents=4995,
)

print(event.model_dump_json(indent=2))
# {
#   "event_id": "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e",
#   "order_id": "a1c2e3f4-5b6d-4e7f-8c9d-0a1b2c3d4e5f",
#   "status": "payment_confirmed",
#   "amount_cents": 4995
# }
# يُسلسل Pydantic v2 كائنات uuid.UUID تلقائيًا كسلسلة نصية
ملاحظة:يُوصى دائمًا بالتحقق من إصدار UUID في بيانات المدخلات الواردة من العملاء أو الأنظمة الخارجية، حتى حين يستخدم النظام المولِّد uuid4()، إذ قد تُرسَل معرّفات من إصدارات أخرى عن طريق الخطأ أو بقصد التلاعب بمسار معالجة البيانات. فحص u.version == 4 سطر واحد يكفي لضمان سلامة الإدخال.

الأخطاء الشائعة عند توليد UUID v4 في Python

رأيت هذه الأنماط الأربعة تظهر في مراجعات الكود وحوادث الإنتاج — من السهل تفويتها لأنها لا ترفع خطأً فورًا. التعرف عليها مسبقًا يوفّر عليك ساعات من تتبع الأخطاء الصامتة التي يصعب إعادة إنتاجها في بيئة الاختبار. بعض هذه الأخطاء، كتشارك UUID واحد بين جميع نسخ الكائن، لا تظهر في الاختبارات الفردية بل تُكتشف فقط في الإنتاج حين تتعامل أنظمة متعددة مع نفس البيانات في آنٍ واحد، مما يجعل التشخيص بالغ الصعوبة.

استدعاء uuid4 بدون أقواس كقيمة افتراضية

المشكلة: تمرير uuid.uuid4 (كائن الدالة) كقيمة افتراضية في dataclass أو نموذج دون تغليفها في default_factory — يُقيّم Python القيمة الافتراضية مرة واحدة عند تعريف الصنف، فتتشارك جميع النسخ نفس UUID.

الحل: استخدم default_factory=uuid.uuid4 في dataclasses أو Field(default_factory=uuid.uuid4) في Pydantic لتوليد UUID جديد لكل نسخة.

After · Python
Before · Python
@dataclass
class Session:
    # صحيح: تُستدعى الدالة لكل نسخة على حدة
    session_id: uuid.UUID = field(default_factory=uuid.uuid4)
@dataclass
class Session:
    # خطأ: يُقيَّم مرة واحدة، جميع النسخ تتشارك هذا UUID
    session_id: uuid.UUID = uuid.uuid4()
مقارنة كائن UUID بسلسلة نصية عادية

المشكلة: لا تساوي كائنات uuid.UUID السلاسل النصية العادية، لذا فإن session_id == '3b1f8a9d-...' يُعيد دائمًا False حتى حين تتطابق القيم — مما يُعطّل عمليات البحث بصمت.

الحل: قارن دائمًا UUID بـ UUID: غلّف السلسلة بـ uuid.UUID() قبل المقارنة، أو حوّل كلا الجانبين إلى str().

After · Python
Before · Python
target = uuid.UUID("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e")
if record["session_id"] == target:   # كلاهما uuid.UUID
    revoke_session(record)

# أو تطبيع كل شيء إلى سلاسل نصية عند الحدود:
if str(record["session_id"]) == str(target):
    revoke_session(record)
# يُعيد False حتى حين تتطابق القيم
if record["session_id"] == "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e":
    revoke_session(record)
تخزين .hex بدلًا من str() مع فقدان الشَرَطات

المشكلة: يُنتج uuid_obj.hex سلسلة مكوّنة من 32 حرفًا بدون شَرَطات. إذا توقّع الكود المنبثق التنسيق القياسي المكوّن من 36 حرفًا بشَرَطات (وهو ما تتوقعه معظم APIs وقواعد البيانات)، سيرفض القيمة أو يُحللها بشكل خاطئ بصمت.

الحل: استخدم str(uuid_obj) للتنسيق القياسي من 36 حرفًا إلا إذا كان لديك متطلب صريح للنموذج المضغوط hex.

After · Python
Before · Python
# يخزن "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" — التنسيق القياسي
payload = {"correlation_id": str(request_id)}
# يخزن "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" — بدون شَرَطات
payload = {"correlation_id": request_id.hex}
استخدام random.random() أو secrets.token_hex() عند توقّع UUID

المشكلة: random.random() غير آمن تشفيريًا، وsecrets.token_hex(16) يُنتج سلسلة hex من 32 حرفًا لا تُعدّ UUID صالحًا — أنظمة التحقق التي تستدعي uuid.UUID() عليها ستُطلق ValueError.

الحل: استخدم uuid.uuid4() في أي وقت يتوقع فيه النظام المستقبِل معرّفًا بتنسيق UUID. استخدم secrets.token_hex() فقط عندما تحتاج صراحةً إلى رمز عشوائي لا يكون بتنسيق UUID.

After · Python
Before · Python
import uuid

request_id = str(uuid.uuid4())  # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"
# UUID v4 صالح وآمن تشفيريًا
import random, secrets

# ليس UUID — سيفشل عند التحقق بـ uuid.UUID()
request_id = secrets.token_hex(16)   # "a1b2c3d4e5f6..."
session_id = str(random.random())    # "0.8273..." — بعيد جدًا

طرق توليد UUID في Python — مقارنة سريعة

جميع الطرق أدناه تُنتج معرّفات من 128 بت لكنها تختلف في مصدر الإنتروبيا وخصائص الخصوصية وما إذا كانت تتطلب تثبيت مكتبة طرف ثالث. اختيار الطريقة الصحيحة يعتمد على متطلبات مشروعك: هل تحتاج إلى خصوصية كاملة بدون بيانات وصفية مكشوفة، أم معرّفات حتمية قابلة للتكرار من مدخلات معروفة، أم أداء أقصى في بيئات عالية الإنتاجية التي تولّد ملايين المعرّفات في الثانية؟ كما يجدر مراعاة حجم التخزين الذي ستشغله المعرّفات في قاعدة البيانات — تشغل السلسلة القياسية 36 بايت بينما تشغل البيانات الثنائية 16 بايت فقط — وما إذا كانت قابلية القراءة البشرية للمعرّفات ضرورية لتسهيل عمليات الدعم الفني وتتبع المشكلات في سجلات الإنتاج.

الطريقة
المصدر
التفرد
آمن للخصوصية
أنواع مخصصة
السرعة
التثبيت
uuid.uuid4()
عشوائي (os.urandom)
2¹²² بت عشوائي
غير مطبق
قياسي
مدمج
uuid.uuid1()
الطابع الزمني + MAC
الوقت + المضيف
❌ (MAC مكشوف)
غير مطبق
قياسي
مدمج
uuid.uuid3(name)
تجزئة MD5
حتمي
namespace+name
قياسي
مدمج
uuid.uuid5(name)
تجزئة SHA-1
حتمي
namespace+name
قياسي
مدمج
uuid_utils.uuid4()
Rust os.urandom
2¹²² بت عشوائي
غير مطبق
~10× أسرع
pip install uuid-utils
secrets.token_hex(16)
os.urandom
128 بت عشوائي
غير مطبق
سريع
مدمج
str(uuid.uuid4())
عشوائي
2¹²² بت عشوائي
غير مطبق
قياسي
مدمج

استخدم uuid.uuid4() للمعرّفات الفريدة للأغراض العامة في تطبيقات الويب والأنظمة الموزعة ومفاتيح قواعد البيانات الأساسية حين لا يكون الترتيب مطلوبًا. استخدم uuid.uuid5() (أو v3) للمعرّفات الحتمية المشتقة من مجال اسم ومجموعة معروفين — مثلًا توليد معرّف ثابت لرابط URL قياسي. انتقل إلى uuid_utils.uuid7() حين تحتاج إلى معرّفات مرتّبة زمنيًا لفهارس قواعد البيانات (تتجنب انقسامات الصفحات في فهارس B-tree عند معدلات إدراج عالية). الجأ إلى uuid_utils.uuid4() حين يكون الاختناق في إنتاجية التوليد الخام.

UUID v4 مقابل UUID v7 — أيهما تختار؟

السؤال العملي الأكثر شيوعًا هو ما إذا كان يجب استخدام UUID v4 أو UUID v7 الأحدث لمفاتيح قواعد البيانات الأساسية. إليك الإجابة المختصرة: استخدم UUID v4 افتراضيًا؛ وانتقل إلى UUID v7 فقط حين يكون تشتت الفهرس مشكلة مقيسة.

قيم UUID v4 عشوائية تمامًا، مما يعني أن عمليات الإدراج تستقر في مواضع عشوائية داخل فهرس B-tree. عند معدلات إدراج معتدلة (مئات إلى آلاف منخفضة في الثانية) هذا مقبول — يتسع الفهرس في مجمّع المخزن المؤقت والكتابات العشوائية رخيصة. عند معدلات إدراج عالية جدًا، يُسبّب التوضع العشوائي انقسامات متكررة للصفحات وإخفاقات ذاكرة التخزين المؤقت، مما يزيد من تضخيم الكتابة ويُبطئ الاستعلامات. بالإضافة إلى ذلك، يتميّز UUID v4 بسهولة الاختبار والتتبع، إذ يمكن تمييز كل معرّف بصريًا بغض النظر عن ترتيب الإدراج أو التسلسل الزمني للأحداث، مما يُبسّط عمليات التدقيق في السجلات وتتبع الطلبات عبر الخدمات الموزعة.

يُضمّن UUID v7 طابعًا زمنيًا بدقة المللي ثانية Unix في أهم البتات، لذا تستقر الصفوف المدرجة في وقت متقارب أيضًا بالقرب من بعضها في الفهرس. يُعطي هذا فهارس B-tree (PostgreSQL وMySQL وSQLite) سلوكًا أقرب إلى عدد صحيح تلقائي التزايد: الصفوف الجديدة تُلحق دائمًا بنهاية الفهرس مما يُلغي انقسامات الصفحات. المقايضة أن UUID v7 يُشفّر طابعًا زمنيًا، مما يكشف وقت الإنشاء — تجنّبه للمعرّفات الموجّهة للمستخدم حين يكون وقت الإنشاء حساسًا.

في Python، UUID v7 ليس في المكتبة القياسية بعد (حتى Python 3.12). ولّده باستخدام pip install uuid-utils واستدعاء uuid_utils.uuid7(). يُعيد كائنًا بنفس مجموعة خصائص uuid.UUID، لذا فإن الانتقال من v4 هو تغيير سطر واحد في مصنع المعرّفات.

للبديل بنقرة واحدة دون أي إعداد لـ Python، الصق سلسلة UUID في مولّد ومتحقق UUID v4 — يولّد ويتحقق ويفكّك جميع الحقول في المتصفح. يمكنك أيضًا توليد مجموعات كبيرة من المعرّفات دفعةً واحدة لملء بيانات الاختبار، أو التحقق من إصدار UUID معيّن ومتغيّره وقيمته السداسية بصمت تام دون الحاجة إلى تشغيل أي سكريبت Python أو إعداد بيئة تطوير محلية.

إن فهم آلية عمل UUID v4 بعمق يساعدك على اتخاذ قرارات تصميمية أفضل عند بناء الأنظمة الموزعة، إذ يمنحك ثقةً بأن المعرّفات المولّدة ستظل فريدةً على مستوى الخوادم المتعددة والبيئات المختلفة.

باختصار، تُعدّ مكتبة uuid في Python أداةً موثوقةً وسهلة الاستخدام لتوليد معرّفات فريدة عالميًا، وتوظيفها بشكل صحيح في مشاريعك يضمن لك قابلية التوسع والتكامل السلس مع الأنظمة الأخرى دون أي تعارض في المعرّفات.

علاوةً على ذلك، يُنصح بتوثيق سياسة توليد المعرّفات في مشروعك، سواء أكانت تعتمد على UUID v4 العشوائي أم على إصدارات أخرى، حتى يتمكن أعضاء الفريق من فهم الخيارات التصميمية بسهولة والالتزام بها عند توسيع قاعدة الكود أو إضافة ميزات جديدة مستقبلاً.

الأسئلة الشائعة

كيف أولّد UUID v4 في Python؟

استدعِ uuid.uuid4() من وحدة uuid المدمجة في Python. تُعيد الدالة كائن UUID — حوّله إلى سلسلة نصية باستخدام str() حين تحتاج إلى تمثيل نصي. الوحدة مضمّنة في المكتبة القياسية فلا حاجة لأي pip install.

Python
import uuid

session_id = uuid.uuid4()
print(session_id)           # e.g. 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
print(str(session_id))      # same canonical string
print(session_id.hex)       # 3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e (no dashes)

ما الفرق بين uuid.uuid4() وstr(uuid.uuid4())؟

تُعيد uuid.uuid4() كائن UUID يمتلك خصائص مثل .hex و.bytes و.int و.version. أما str(uuid.uuid4()) فتحوّل ذلك الكائن فورًا إلى سلسلة نصية من 36 حرفًا بالتنسيق القياسي، متجاهلةً الكائن. احتفظ بالكائن إذا احتجت إلى تمثيلات متعددة؛ وحوّله إلى سلسلة عند الحدود التي تمرر فيها القيمة إلى حمولة JSON أو قاعدة بيانات أو رأس HTTP.

Python
import uuid

u = uuid.uuid4()
print(type(u))          # <class 'uuid.UUID'>
print(u.version)        # 4
print(u.hex)            # 32-char hex, no dashes
print(u.bytes)          # 16-byte binary
print(str(u))           # canonical 36-char string with dashes

هل uuid.uuid4() آمن تشفيريًا؟

نعم. تستخدم uuid.uuid4() في Python داخليًا os.urandom()، التي تقرأ من مولّد الأعداد العشوائية الآمن تشفيريًا في نظام التشغيل (/dev/urandom على Linux/macOS، وCryptGenRandom على Windows). تجعل الـ 122 بتًا العشوائية احتمالية التصادم ضئيلة جدًا لأي عبء عمل واقعي. لا تخلطها مع random.random() التي ليست آمنة تشفيريًا.

Python
import uuid, os

# uuid4 internally calls os.urandom(16)
raw = os.urandom(16)
# uuid4 sets the version and variant bits before returning
u = uuid.UUID(bytes=raw, version=4)
print(u)  # valid v4 UUID from raw random bytes

كيف أتحقق من صحة سلسلة UUID v4 في Python؟

حلّل القيمة باستخدام uuid.UUID() وتحقق من خاصية .version. إذا لم تكن السلسلة UUID صحيحًا، يرفع uuid.UUID() استثناء ValueError — اصطده لمعالجة المدخلات غير الصالحة. يتحقق هذا أيضًا من صحة التنسيق (الشَرَطات والطول).

Python
import uuid

def is_valid_uuid4(value: str) -> bool:
    try:
        u = uuid.UUID(value)
        return u.version == 4
    except ValueError:
        return False

print(is_valid_uuid4("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"))  # True
print(is_valid_uuid4("not-a-uuid"))                              # False
print(is_valid_uuid4("3b1f8a9d-2c4e-1f6a-8b0d-5e7c9f1a3d2e"))  # False (v1, not v4)

كيف أخزّن UUIDs في قاعدة بيانات PostgreSQL أو SQLite من Python؟

مع PostgreSQL (عبر psycopg2 أو asyncpg)، مرّر كائن UUID مباشرةً — يتكيّف المشغّل تلقائيًا مع نوع UUID الأصلي. أما SQLite، التي لا تملك نوع UUID أصليًا، فخزّن فيها البيانات كـ TEXT باستخدام str(uuid_obj) أو كـ BLOB باستخدام uuid_obj.bytes. تمتلك SQLAlchemy نوع عمود UUID يعالج هذا تلقائيًا عبر جميع قواعد البيانات.

Python
import uuid
import sqlite3

conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE events (id TEXT PRIMARY KEY, name TEXT)")

event_id = uuid.uuid4()
conn.execute("INSERT INTO events VALUES (?, ?)", (str(event_id), "user_signup"))
conn.commit()

row = conn.execute("SELECT * FROM events").fetchone()
# Reconstruct UUID object from stored string
retrieved_id = uuid.UUID(row[0])
print(retrieved_id.version)  # 4

هل يمكنني توليد عدة UUIDs دفعةً واحدة في Python؟

نعم — استخدم list comprehension أو مولّدًا. كل استدعاء لـ uuid.uuid4() مستقل ومضمون أن ينتج قيمة مميزة. لتوليد قوائم ضخمة بكفاءة في الذاكرة، فضّل تعبير المولّد (generator expression) بدلًا من list comprehension حتى لا تُخصَّص الذاكرة للقائمة بأسرها في وقت واحد. إضافةً إلى ذلك، يمكنك تخزين المعرّفات المُولَّدة في مجموعة (set) للتحقق الفوري من التفرد عند دمجها مع معرّفات من مصادر أخرى، مما يُضيف طبقة أمان إضافية في سير عمل معالجة البيانات. لتوليد كميات كبيرة حيث يهم الأداء، فإن uuid-utils (مدعوم بـ Rust) أسرع بنحو 10 أضعاف من المكتبة القياسية.

Python
import uuid

# Generate 5 unique trace IDs for a batch request
trace_ids = [str(uuid.uuid4()) for _ in range(5)]
for tid in trace_ids:
    print(tid)
# Each line is a distinct UUID v4

أدوات ذات صلة

  • UUID v4 Generatorتوليد قيم UUID v4 فورًا في المتصفح — دون الحاجة إلى بيئة Python. انسخ قيمة واحدة أو ولّد المئات دفعةً واحدة.
  • UUID v7 Generatorتوليد قيم UUID v7 مرتبة زمنيًا — قابلة للفرز حسب وقت الإنشاء، مثالية لمفاتيح قواعد البيانات الأساسية حيث يهم تشتت الفهرس.
  • UUID Decoderفحص أي UUID — الإصدار، المتغيّر، الطابع الزمني (v1/v7)، وحقول العقدة — دون كتابة محلّل من الصفر.
  • JWT Decoderفك تشفير وفحص رموز JWT، التي كثيرًا ما تحمل مطالبات UUID للموضوع (sub) أو معرّفات jti إلى جانب UUIDs الجلسات.
MS
Maria SantosBackend Developer

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.

DV
Dmitri Volkovالمراجع التقني

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.