NewType, TypeVar અને જેનેરિક અવરોધો સાથે અદ્યતન પાયથોન ટાઇપિંગમાં ઊંડાણપૂર્વક જાણો. વધુ મજબૂત, વાંચવા યોગ્ય અને જાળવી શકાય તેવી એપ્લિકેશન્સ બનાવતા શીખો.
પાયથોનના ટાઇપિંગ એક્સ્ટેન્શન્સમાં નિપુણતા: NewType, TypeVar અને જેનેરિક અવરોધો માટેની માર્ગદર્શિકા
આધુનિક સોફ્ટવેર ડેવલપમેન્ટની દુનિયામાં, કોડ લખવો કે જે માત્ર કાર્યક્ષમ જ નહીં પણ સ્પષ્ટ, જાળવી શકાય તેવો અને મજબૂત હોય તે સર્વોપરી છે. પાયથોન, પરંપરાગત રીતે ગતિશીલ રીતે ટાઇપ થયેલી ભાષા હોવા છતાં, PEP 484 માં રજૂ કરાયેલ તેની શક્તિશાળી ટાઇપિંગ સિસ્ટમ દ્વારા આ ફિલસૂફીને અપનાવી છે. જ્યારે int
, str
અને list
જેવી મૂળભૂત ટાઇપ હિન્ટ્સ હવે સામાન્ય છે, ત્યારે પાયથોન ટાઇપિંગની સાચી શક્તિ તેની અદ્યતન સુવિધાઓમાં રહેલી છે. આ સાધનો વિકાસકર્તાઓને જટિલ સંબંધો અને અવરોધો વ્યક્ત કરવાની મંજૂરી આપે છે, જે વધુ સુરક્ષિત અને સ્વ-દસ્તાવેજીકરણ કોડ તરફ દોરી જાય છે.
આ લેખ typing
મોડ્યુલની ત્રણ સૌથી પ્રભાવશાળી સુવિધાઓમાં ઊંડાણપૂર્વક ધ્યાન કેન્દ્રિત કરે છે: NewType
, TypeVar
, અને તેના પર લાગુ કરી શકાય તેવા અવરોધો. આ ખ્યાલોમાં નિપુણતા મેળવીને, તમે તમારા પાયથોન કોડને માત્ર કાર્યાત્મકથી વ્યાવસાયિક રીતે એન્જિનિયર્ડ સુધી ઉન્નત કરી શકો છો, ઉત્પાદનમાં પહોંચે તે પહેલાં જ સૂક્ષ્મ ભૂલોને પકડી શકો છો.
શા માટે અદ્યતન ટાઇપિંગ મહત્વપૂર્ણ છે
વિશિષ્ટતાઓનું અન્વેષણ કરીએ તે પહેલાં, ચાલો સ્થાપિત કરીએ કે શા માટે મૂળભૂત પ્રકારોથી આગળ વધવું એ રમત બદલનાર છે. મોટા પાયે એપ્લિકેશન્સમાં, સરળ પ્રિમિટિવ પ્રકારો ઘણીવાર તેઓ રજૂ કરતા ડેટાનો સંપૂર્ણ અર્થપૂર્ણ અર્થ કેપ્ચર કરવામાં નિષ્ફળ જાય છે. શું int
એ વપરાશકર્તા ID, ઉત્પાદન ગણતરી અથવા મીટરમાં માપન છે? સંદર્ભ વિના, તે માત્ર સંખ્યાઓ છે, અને કમ્પાઇલર અથવા ઇન્ટરપ્રિટર તમને આકસ્મિક રીતે એકનો ઉપયોગ કરવાથી રોકી શકતું નથી જ્યાં બીજું અપેક્ષિત છે.
અદ્યતન ટાઇપિંગ આ વ્યવસાયિક તર્ક અને ડોમેન જ્ઞાનને સીધા તમારા કોડના માળખામાં એમ્બેડ કરવાનો માર્ગ પૂરો પાડે છે. આનાથી પરિણમે છે:
- કોડ સ્પષ્ટતામાં વધારો: પ્રકારો દસ્તાવેજીકરણના એક સ્વરૂપ તરીકે કાર્ય કરે છે, જે ફંક્શન સિગ્નેચરને તરત જ સમજી શકાય તેવા બનાવે છે.
- સુધારેલ IDE સપોર્ટ: VS Code, PyCharm અને અન્ય જેવા ટૂલ્સ વધુ સચોટ ઑટોકમ્પ્લેશન, રિફેક્ટરિંગ સપોર્ટ અને રીઅલ-ટાઇમ એરર ડિટેક્શન પ્રદાન કરી શકે છે.
- પ્રારંભિક બગ શોધ: Mypy, Pyright અથવા Pyre જેવા સ્ટેટિક ટાઇપ ચેકર્સ તમારા કોડનું વિશ્લેષણ કરી શકે છે અને વિકાસ દરમિયાન સંભવિત રનટાઇમ ભૂલોના સંપૂર્ણ વર્ગને ઓળખી શકે છે.
- વધુ જાળવણીક્ષમતા: જેમ જેમ કોડબેઝ વધે છે, તેમ તેમ મજબૂત ટાઇપિંગ નવા વિકાસકર્તાઓ માટે સિસ્ટમની ડિઝાઇનને સમજવા અને આત્મવિશ્વાસ સાથે ફેરફારો કરવાનું સરળ બનાવે છે.
હવે, ચાલો આપણા પ્રથમ સાધન: NewType
નું અન્વેષણ કરીને આ શક્તિને અનલૉક કરીએ.
NewType: સિમેન્ટીક સલામતી માટે વિશિષ્ટ પ્રકારો બનાવવું
સમસ્યા: પ્રિમિટિવ ઓબ્સેશન
સોફ્ટવેર ડેવલપમેન્ટમાં એક સામાન્ય એન્ટિ-પેટર્ન છે "પ્રિમિટિવ ઓબ્સેશન"—ડોમેન-વિશિષ્ટ ખ્યાલોને રજૂ કરવા માટે બિલ્ટ-ઇન પ્રિમિટિવ પ્રકારોનો વધુ પડતો ઉપયોગ. એક સિસ્ટમ ધ્યાનમાં લો જે વપરાશકર્તા અને ઓર્ડર માહિતીને હેન્ડલ કરે છે:
def process_order(user_id: int, order_id: int) -> None:
print(f"Processing order {order_id} for user {user_id}...")
# A simple, but potentially disastrous, mistake
user_identification = 101
order_identification = 4512
process_order(order_identification, user_identification) # Whoops!
# Output: Processing order 101 for user 4512...
ઉપરોક્ત ઉદાહરણમાં, અમે આકસ્મિક રીતે user_id
અને order_id
ને અદલાબદલી કર્યા છે. પાયથોન ફરિયાદ કરશે નહીં કારણ કે બંને પૂર્ણાંક છે. સ્ટેટિક ટાઇપ ચેકર પણ તે જ કારણસર તેને પકડશે નહીં. આ પ્રકારની ભૂલ કપટી હોઈ શકે છે, જે ડેટા ભ્રષ્ટ કરવા અથવા ખોટા વ્યવસાયિક કામગીરી તરફ દોરી જાય છે.
ઉકેલ: `NewType` નો પરિચય
NewType
તમને હાલના પ્રકારોમાંથી વિશિષ્ટ, નોમિનલ પ્રકારો બનાવવાની મંજૂરી આપીને આ સમસ્યાનું નિરાકરણ લાવે છે. આ નવા પ્રકારોને સ્ટેટિક ટાઇપ ચેકર્સ દ્વારા અનન્ય ગણવામાં આવે છે પરંતુ શૂન્ય રનટાઇમ ઓવરહેડ હોય છે—રનટાઇમ પર, તેઓ તેમના અંતર્ગત મૂળભૂત પ્રકારની જેમ જ વર્તે છે.
ચાલો NewType
નો ઉપયોગ કરીને અમારા ઉદાહરણને રિફેક્ટર કરીએ:
from typing import NewType
# Define distinct types for User IDs and Order IDs
UserId = NewType('UserId', int)
OrderId = NewType('OrderId', int)
def process_order(user_id: UserId, order_id: OrderId) -> None:
print(f"Processing order {order_id} for user {user_id}...")
user_identification = UserId(101)
order_identification = OrderId(4512)
# Correct usage - works perfectly
process_order(user_identification, order_identification)
# Incorrect usage - now caught by a static type checker!
# Mypy will raise an error like:
# error: Argument 1 to "process_order" has incompatible type "OrderId"; expected "UserId"
# error: Argument 2 to "process_order" has incompatible type "UserId"; expected "OrderId"
process_order(order_identification, user_identification)
NewType
સાથે, અમે ટાઇપ ચેકરને જણાવ્યું છે કે UserId
અને OrderId
એકબીજા સાથે બદલી શકાય તેવા નથી, ભલે તેઓ તેમના મૂળમાં બંને પૂર્ણાંક હોય. આ સરળ ફેરફાર સલામતીનો એક શક્તિશાળી સ્તર ઉમેરે છે.
`NewType` વિરુદ્ધ `TypeAlias`
NewType
ને સરળ ટાઇપ ઉપનામથી અલગ પાડવું મહત્વપૂર્ણ છે. ટાઇપ ઉપનામ ફક્ત હાલના પ્રકારને નવું નામ આપે છે પરંતુ એક વિશિષ્ટ પ્રકાર બનાવતું નથી:
from typing import TypeAlias
# This is just an alias. A type checker sees UserIdAlias as exactly the same as int.
UserIdAlias: TypeAlias = int
def process_user(user_id: UserIdAlias) -> None:
...
# No error here, because UserIdAlias is just an int
process_user(123)
process_user(OrderId(999)) # OrderId is also an int at runtime
`TypeAlias` નો ઉપયોગ કરો જ્યારે પ્રકારો એકબીજા સાથે બદલી શકાય તેવા હોય (દા.ત., `Vector = list[float]`) ત્યારે વાંચનીયતા માટે. `NewType` નો ઉપયોગ કરો જ્યારે પ્રકારો વૈચારિક રીતે અલગ હોય અને તેને મિશ્રિત ન કરવા જોઈએ ત્યારે સલામતી માટે.
TypeVar: શક્તિશાળી જેનેરિક ફંક્શન્સ અને ક્લાસિસની ચાવી
મોટેભાગે, આપણે એવા કાર્યો અથવા વર્ગો લખીએ છીએ જે વિવિધ પ્રકારો પર કાર્ય કરવા માટે ડિઝાઇન કરવામાં આવ્યા છે જ્યારે તેમની વચ્ચેના સંબંધો જાળવી રાખે છે. ઉદાહરણ તરીકે, સૂચિનું પ્રથમ ઘટક પરત કરતું કાર્ય સ્ટ્રિંગ્સની સૂચિ આપવામાં આવે તો સ્ટ્રિંગ પરત કરવું જોઈએ, અને પૂર્ણાંકોની સૂચિ આપવામાં આવે તો પૂર્ણાંક પરત કરવું જોઈએ.
`Any` સાથેની સમસ્યા
એક નિષ્કપટ અભિગમ typing.Any
નો ઉપયોગ કરી શકે છે, જે તે ચલ માટે ટાઇપ ચેકિંગને અસરકારક રીતે અક્ષમ કરે છે.
from typing import Any, List
def get_first_element_any(items: List[Any]) -> Any:
if items:
return items[0]
return None
numbers = [1, 2, 3]
first_num = get_first_element_any(numbers)
# What is the type of 'first_num'? The type checker only knows 'Any'.
# This means we lose autocompletion and type safety.
# (first_num.imag) # No static error, but a runtime AttributeError!
Any
નો ઉપયોગ કરવાથી આપણે સ્ટેટિક ટાઇપિંગના ફાયદાઓનો ત્યાગ કરવા મજબૂર થઈએ છીએ. ટાઇપ ચેકર ફંક્શનમાંથી પરત કરાયેલ મૂલ્ય વિશેની બધી માહિતી ગુમાવે છે.
ઉકેલ: `TypeVar` નો પરિચય
TypeVar
એ એક ખાસ ચલ છે જે એક પ્રકાર માટે પ્લેસહોલ્ડર તરીકે કાર્ય કરે છે. તે આપણને ફંક્શન આર્ગ્યુમેન્ટ્સના પ્રકારો અને તેમના પરત મૂલ્યો વચ્ચેના સંબંધો જાહેર કરવાની મંજૂરી આપે છે. આ પાયથોનમાં જેનેરિક્સનો પાયો છે.
ચાલો TypeVar
નો ઉપયોગ કરીને આપણા ફંક્શનને ફરીથી લખીએ:
from typing import TypeVar, List, Optional
# Create a TypeVar. The string 'T' is a convention.
T = TypeVar('T')
def get_first_element(items: List[T]) -> Optional[T]:
if items:
return items[0]
return None
# --- Usage Examples ---
# Example 1: List of integers
numbers = [10, 20, 30]
first_num = get_first_element(numbers)
# Mypy correctly infers that 'first_num' is of type 'Optional[int]'
# Example 2: List of strings
names = ["Alice", "Bob", "Charlie"]
first_name = get_first_element(names)
# Mypy correctly infers that 'first_name' is of type 'Optional[str]'
# Now, the type checker can help us!
if first_num is not None:
print(first_num + 5) # OK, it's an int!
if first_name is not None:
print(first_name.upper()) # OK, it's a str!
ઇનપુટ (List[T]
) અને આઉટપુટ (Optional[T]
) બંનેમાં T
નો ઉપયોગ કરીને, અમે એક લિંક બનાવી છે. ટાઇપ ચેકર સમજે છે કે ઇનપુટ સૂચિ માટે T
જે પણ પ્રકાર સાથે ઇન્સ્ટન્ટિએટ થયેલ છે, તે જ પ્રકાર ફંક્શન દ્વારા પરત કરવામાં આવશે. આ જેનેરિક પ્રોગ્રામિંગનો સાર છે.
જેનેરિક ક્લાસિસ
TypeVar
જેનેરિક ક્લાસિસ બનાવવા માટે પણ આવશ્યક છે. આ કરવા માટે, તમારો ક્લાસ typing.Generic
માંથી વારસો મેળવવો જોઈએ.
from typing import TypeVar, Generic, List
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: List[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def is_empty(self) -> bool:
return not self._items
# Create a stack specifically for integers
int_stack = Stack[int]()
int_stack.push(10)
int_stack.push(20)
value = int_stack.pop() # 'value' is correctly inferred as 'int'
# int_stack.push("hello") # Mypy error: Expected 'int', got 'str'
# Create a stack specifically for strings
str_stack = Stack[str]()
str_stack.push("hello")
# str_stack.push(123) # Mypy error: Expected 'str', got 'int'
જેનેરિક્સને આગળ લઈ જવું: `TypeVar` પર અવરોધો
અનિયંત્રિત TypeVar
કોઈપણ પ્રકાર માટે ઊભું રહી શકે છે, જે શક્તિશાળી છે પરંતુ ક્યારેક ખૂબ છૂટછાટ આપનારું છે. જો અમારા જેનેરિક ફંક્શનને તેના ઇનપુટ્સ પર સરવાળો, સરખામણી અથવા કોઈ ચોક્કસ પદ્ધતિને કૉલ કરવા જેવી કામગીરી કરવાની જરૂર હોય તો શું? અનિયંત્રિત TypeVar
કામ કરશે નહીં કારણ કે ટાઇપ ચેકર પાસે કોઈ ગેરંટી નથી કે કોઈપણ આપેલ પ્રકાર T
તે કામગીરીને સપોર્ટ કરશે.
આ તે જગ્યા છે જ્યાં અવરોધો આવે છે. તેઓ આપણને TypeVar
રજૂ કરી શકે તેવા પ્રકારોને પ્રતિબંધિત કરવાની મંજૂરી આપે છે.
અવરોધ પ્રકાર 1: `bound`
એક `bound` એ `TypeVar` માટે ઉપલા બાઉન્ડ નિર્દિષ્ટ કરે છે. આનો અર્થ એ છે કે `TypeVar` પોતે બાઉન્ડ પ્રકાર અથવા તેના કોઈપણ સબટાઇપ્સ હોઈ શકે છે. જ્યારે તમારે ખાતરી કરવાની જરૂર હોય કે પ્રકાર કોઈ ચોક્કસ બેઝ ક્લાસની પદ્ધતિઓ અને વિશેષતાઓને સપોર્ટ કરે છે ત્યારે આ ઉપયોગી છે.
બે તુલનાત્મક વસ્તુઓમાંથી મોટાને શોધતું કાર્ય ધ્યાનમાં લો. `>` ઓપરેટર બધા પ્રકારો માટે વ્યાખ્યાયિત નથી.
from typing import TypeVar
# This version causes a type error!
T = TypeVar('T')
def find_larger(a: T, b: T) -> T:
# Mypy error: Unsupported operand types for > ("T" and "T")
return a if a > b else b
અમે આને `bound` નો ઉપયોગ કરીને ઠીક કરી શકીએ છીએ. કારણ કે int
અને float
જેવા સંખ્યાત્મક પ્રકારો સરખામણીને સપોર્ટ કરે છે, અમે `float` ને બાઉન્ડ તરીકે ઉપયોગ કરી શકીએ છીએ (જેમ કે ટાઇપિંગ વિશ્વમાં `int` એ `float` નો સબટાઇપ છે).
from typing import TypeVar
# Create a bounded TypeVar
Number = TypeVar('Number', bound=float)
def find_larger(a: Number, b: Number) -> Number:
# This is now type-safe! The checker knows 'Number' supports '>'
return a if a > b else b
find_larger(10, 20) # OK, T is int
find_larger(3.14, 1.618) # OK, T is float
# find_larger("a", "b") # Mypy error: Type 'str' is not a subtype of 'float'
bound=float
ટાઇપ ચેકરને ખાતરી આપે છે કે Number
માટે બદલાયેલ કોઈપણ પ્રકાર float
ની પદ્ધતિઓ અને વર્તણૂકો ધરાવશે, જેમાં સરખામણી ઓપરેટર્સનો સમાવેશ થાય છે.
અવરોધ પ્રકાર 2: મૂલ્ય અવરોધો
કેટલીકવાર, તમે `TypeVar` ને ક્લાસ હાયરાર્કી સુધી મર્યાદિત કરવા માંગતા નથી, પરંતુ સંભવિત પ્રકારોની ચોક્કસ, ગણતરી કરેલ સૂચિ સુધી મર્યાદિત કરવા માંગો છો. આ માટે, તમે `TypeVar` કન્સ્ટ્રક્ટરને સીધા જ બહુવિધ પ્રકારો પસાર કરી શકો છો.
એક ફંક્શનની કલ્પના કરો જે `str` અથવા `bytes` બંનેને પ્રોસેસ કરી શકે છે પરંતુ બીજું કંઈ નહીં. અહીં `bound` યોગ્ય નથી કારણ કે `str` અને `bytes` આપણા હેતુઓ માટે અનુકૂળ, ચોક્કસ બેઝ ક્લાસ શેર કરતા નથી.
from typing import TypeVar
# Create a TypeVar constrained to 'str' and 'bytes'
StrOrBytes = TypeVar('StrOrBytes', str, bytes)
def get_hash(data: StrOrBytes) -> int:
# Both str and bytes have an __hash__ method, so this is safe.
return hash(data)
get_hash("hello world") # OK, StrOrBytes is str
get_hash(b"hello world") # OK, StrOrBytes is bytes
# get_hash(123) # Mypy error: Value of type variable "StrOrBytes" of "get_hash"
# # cannot be "int"
આ `bound` કરતાં વધુ સચોટ છે. તે ટાઇપ ચેકરને કહે છે કે `StrOrBytes` *ચોક્કસપણે* `str` અથવા `bytes` હોવું જોઈએ, કોઈ સામાન્ય પૂર્વજનું સબટાઇપ નહીં.
બધું એકસાથે મૂકવું: એક વ્યવહારુ દૃશ્ય
ચાલો આ ખ્યાલોને જોડીને એક નાની, ટાઇપ-સુરક્ષિત ડેટા પ્રોસેસિંગ યુટિલિટી બનાવીએ. અમારો ધ્યેય એક એવું ફંક્શન બનાવવાનો છે જે વસ્તુઓની સૂચિ લે છે, તેમાંથી દરેકમાંથી એક ચોક્કસ વિશેષતા કાઢે છે, અને તે વિશેષતાના અનન્ય મૂલ્યો જ પરત કરે છે.
import dataclasses
from typing import TypeVar, List, Set, Hashable, NewType
# 1. Use NewType for semantic clarity
ProductId = NewType('ProductId', int)
# 2. Define a data structure
@dataclasses.dataclass
class Product:
id: ProductId
name: str
category: str
# 3. Use a bounded TypeVar. The attribute we extract must be hashable
# to be put into a set for uniqueness.
HashableValue = TypeVar('HashableValue', bound=Hashable)
def get_unique_attributes(items: List[Product], attribute_name: str) -> Set[HashableValue]:
"""Extracts a unique set of attribute values from a list of products."""
unique_values: Set[HashableValue] = set()
for item in items:
value = getattr(item, attribute_name)
# A static checker can't verify 'value' is HashableValue here without
# more complex plugins, but the bound documents our intent and helps consumers.
unique_values.add(value)
return unique_values
# --- Usage ---
products = [
Product(id=ProductId(1), name="Laptop", category="Electronics"),
Product(id=ProductId(2), name="Mouse", category="Electronics"),
Product(id=ProductId(3), name="Desk Chair", category="Furniture"),
]
# Get unique categories. The type checker knows the return is Set[str]
unique_categories: Set[str] = get_unique_attributes(products, 'category')
print(f"Unique Categories: {unique_categories}")
# Get unique product IDs. The return is Set[ProductId]
unique_ids: Set[ProductId] = get_unique_attributes(products, 'id')
print(f"Unique IDs: {unique_ids}")
આ ઉદાહરણમાં:
NewType
આપણનેProductId
આપે છે, જે આપણને અન્ય પૂર્ણાંકો સાથે આકસ્મિક રીતે મિશ્રિત થવાથી અટકાવે છે.TypeVar('...', bound=Hashable)
તે જટિલ આવશ્યકતાને દસ્તાવેજીકૃત કરે છે અને લાગુ કરે છે કે આપણે જે વિશેષતા કાઢીએ છીએ તે હેશ કરી શકાય તેવી હોવી જોઈએ, કારણ કે આપણે તેનેSet
માં ઉમેરી રહ્યા છીએ.- ફંક્શન સિગ્નેચર
-> Set[HashableValue]
, જેનેરિક હોવા છતાં, વિકાસકર્તાઓ અને ટૂલ્સને ફંક્શનના વર્તન વિશે એક મજબૂત સંકેત પૂરો પાડે છે.
નિષ્કર્ષ: કોડ લખો જે માણસો અને મશીનો બંને માટે કાર્ય કરે છે
ઉચ્ચ-ગુણવત્તાવાળા સોફ્ટવેરની શોધમાં પાયથોનની ટાઇપિંગ સિસ્ટમ એક શક્તિશાળી સાથી છે. મૂળભૂત બાબતોથી આગળ વધીને અને NewType
, TypeVar
અને જેનેરિક અવરોધો જેવા સાધનો અપનાવીને, તમે એવો કોડ લખી શકો છો જે નોંધપાત્ર રીતે વધુ સુરક્ષિત, સમજવામાં સરળ અને જાળવવા માટે સરળ હોય.
- `NewType` નો ઉપયોગ કરો પ્રિમિટિવ પ્રકારોને સિમેન્ટીક અર્થ આપવા અને વિવિધ ખ્યાલોને મિશ્રિત કરવાથી થતી તાર્કિક ભૂલોને અટકાવવા.
- `TypeVar` નો ઉપયોગ કરો લવચીક, ફરીથી વાપરી શકાય તેવા જેનેરિક ફંક્શન્સ અને ક્લાસિસ બનાવવા માટે જે ટાઇપ માહિતીને જાળવી રાખે છે.
- `TypeVar` પર `bound` અને મૂલ્ય અવરોધોનો ઉપયોગ કરો તમારા જેનેરિક પ્રકારો પરની આવશ્યકતાઓને લાગુ કરવા માટે, ખાતરી કરો કે તેઓ તમને કરવા જરૂરી કામગીરીઓને સમર્થન આપે છે.
આ પેટર્ન અપનાવવું શરૂઆતમાં વધારાનું કામ લાગી શકે છે, પરંતુ ઘટક ભૂલો, સુધારેલ સહયોગ અને ઉન્નત વિકાસકર્તા ઉત્પાદકતામાં લાંબા ગાળાનો ફાયદો અપાર છે. આજે જ તેમને તમારા પ્રોજેક્ટ્સમાં સામેલ કરવાનું શરૂ કરો અને વધુ મજબૂત અને વ્યાવસાયિક પાયથોન એપ્લિકેશન્સ માટે પાયો બનાવો.