توليد UUID v4 في Python — uuid.uuid4()
استخدم مولّد 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 الخالية من الخادم.
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.
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 جديدًا في كل استدعاء:
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 عند الطلب بدلًا من تخصيص جميع القيم في الذاكرة مسبقًا:
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 بت. اختيار التمثيل الصحيح لطبقة التخزين يمنع الفساد الصامت للبيانات والبايتات المهدورة.
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 الضعيفة.
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() في جميع أنحاء الكود.
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= أبسط من إنشاء فئة فرعية:
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 إلى كائنات لتحصل على مجموعة الخصائص الكاملة وأمان الأنواع:
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 على القرص — مثلًا تدوير معرّف الارتباط في ملف إعداد أو بيانات تجريبية — اقرأه وعدّله واكتبه مجددًا بشكل ذري:
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 كامل.
# 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"
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 أضعاف المكتبة القياسية.
pip install 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
isinstance(u, uuid.UUID) الصارمة من المكتبة القياسية، استخدم وضع التوافق: import uuid_utils.compat as uuid. وضع التوافق أبطأ قليلًا من الوضع الافتراضي لكنه لا يزال أسرع من المكتبة القياسية.UUID v4 في Dataclasses ونماذج Pydantic
تدعم Python dataclasses ونماذج Pydantic حقول UUID بشكل أصلي. النمط الأساسي عند استخدام UUID كقيمة افتراضية مُولَّدة تلقائيًا هو تمرير مرجع الدالة وليس نتيجة استدعائها — وإلا ستتشارك جميع النسخ نفس 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) # Falsefrom 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 تلقائيًا كسلسلة نصيةuuid4()، إذ قد تُرسَل معرّفات من إصدارات أخرى عن طريق الخطأ أو بقصد التلاعب بمسار معالجة البيانات. فحص u.version == 4 سطر واحد يكفي لضمان سلامة الإدخال.الأخطاء الشائعة عند توليد UUID v4 في Python
رأيت هذه الأنماط الأربعة تظهر في مراجعات الكود وحوادث الإنتاج — من السهل تفويتها لأنها لا ترفع خطأً فورًا. التعرف عليها مسبقًا يوفّر عليك ساعات من تتبع الأخطاء الصامتة التي يصعب إعادة إنتاجها في بيئة الاختبار. بعض هذه الأخطاء، كتشارك UUID واحد بين جميع نسخ الكائن، لا تظهر في الاختبارات الفردية بل تُكتشف فقط في الإنتاج حين تتعامل أنظمة متعددة مع نفس البيانات في آنٍ واحد، مما يجعل التشخيص بالغ الصعوبة.
المشكلة: تمرير uuid.uuid4 (كائن الدالة) كقيمة افتراضية في dataclass أو نموذج دون تغليفها في default_factory — يُقيّم Python القيمة الافتراضية مرة واحدة عند تعريف الصنف، فتتشارك جميع النسخ نفس UUID.
الحل: استخدم default_factory=uuid.uuid4 في dataclasses أو Field(default_factory=uuid.uuid4) في Pydantic لتوليد UUID جديد لكل نسخة.
@dataclass
class Session:
# صحيح: تُستدعى الدالة لكل نسخة على حدة
session_id: uuid.UUID = field(default_factory=uuid.uuid4)@dataclass
class Session:
# خطأ: يُقيَّم مرة واحدة، جميع النسخ تتشارك هذا UUID
session_id: uuid.UUID = uuid.uuid4()المشكلة: لا تساوي كائنات uuid.UUID السلاسل النصية العادية، لذا فإن session_id == '3b1f8a9d-...' يُعيد دائمًا False حتى حين تتطابق القيم — مما يُعطّل عمليات البحث بصمت.
الحل: قارن دائمًا UUID بـ UUID: غلّف السلسلة بـ uuid.UUID() قبل المقارنة، أو حوّل كلا الجانبين إلى str().
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)المشكلة: يُنتج uuid_obj.hex سلسلة مكوّنة من 32 حرفًا بدون شَرَطات. إذا توقّع الكود المنبثق التنسيق القياسي المكوّن من 36 حرفًا بشَرَطات (وهو ما تتوقعه معظم APIs وقواعد البيانات)، سيرفض القيمة أو يُحللها بشكل خاطئ بصمت.
الحل: استخدم str(uuid_obj) للتنسيق القياسي من 36 حرفًا إلا إذا كان لديك متطلب صريح للنموذج المضغوط hex.
# يخزن "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" — التنسيق القياسي
payload = {"correlation_id": str(request_id)}# يخزن "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" — بدون شَرَطات
payload = {"correlation_id": request_id.hex}المشكلة: random.random() غير آمن تشفيريًا، وsecrets.token_hex(16) يُنتج سلسلة hex من 32 حرفًا لا تُعدّ UUID صالحًا — أنظمة التحقق التي تستدعي uuid.UUID() عليها ستُطلق ValueError.
الحل: استخدم uuid.uuid4() في أي وقت يتوقع فيه النظام المستقبِل معرّفًا بتنسيق UUID. استخدم secrets.token_hex() فقط عندما تحتاج صراحةً إلى رمز عشوائي لا يكون بتنسيق UUID.
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() للمعرّفات الفريدة للأغراض العامة في تطبيقات الويب والأنظمة الموزعة ومفاتيح قواعد البيانات الأساسية حين لا يكون الترتيب مطلوبًا. استخدم 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.
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.
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() التي ليست آمنة تشفيريًا.
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 — اصطده لمعالجة المدخلات غير الصالحة. يتحقق هذا أيضًا من صحة التنسيق (الشَرَطات والطول).
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 يعالج هذا تلقائيًا عبر جميع قواعد البيانات.
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 أضعاف من المكتبة القياسية.
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 الجلسات.
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.
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.