PythonPython · Chapterبەش 11 · Lectureوانە 11.2

Attributes and Methodsخەسڵەتەکان و میتۆدەکان

Instance attributes and class attributes, shadowing and the shared-list trap; instance methods that change an object or return a value; __str__ and __repr__; comparing objects with __eq__ and __hash__; special methods such as __len__; class methods and static methods with @classmethod and @staticmethod; and a small school program with Student, Teacher and Course classes.

و ، و تەڵەی لیستی هاوبەش؛ کە دەگۆڕن یان دەگەڕێننەوە؛ __str__ و __repr__؛ بەراوردکردنی بە __eq__ و __hash__؛ وەک __len__؛ و ستاتیک بە @classmethod و @staticmethod؛ و بچووکی قوتابخانە بە Student، Teacher و Course.

21 min read19 خولەک خوێندنەوە · 22 stepsهەنگاو

What you will learnچی فێر دەبیت

  • Tell instance attributes from class attributes, change class attributes through the class, and avoid shadowing and shared lists.
  • Write instance methods that check and change an object's state or return a value.
  • Write __str__ and __repr__, and know which calls use each.
  • Define equality with __eq__ (and a matching __hash__), and tell == from is.
  • Use @classmethod for alternative constructors and @staticmethod for helpers, and choose the right kind of method.
  • Read and extend a small program in which several classes work together.
  • لە جیا بکەیتەوە، لە ڕێگەی بگۆڕیت و خۆت لە و لیستی هاوبەش بپارێزیت.
  • بنووسیت کە دۆخی بپشکنن و بگۆڕن یان بگەڕێننەوە.
  • __str__ و __repr__ بنووسیت و بزانیت کام بانگکردنەکان هەریەکەیان بەکار دەهێنن.
  • یەکسانی بە __eq__ (و __hash__ـێکی گونجاو) پێناسە بکەیت و == لە is جیا بکەیتەوە.
  • @classmethod بۆ جێگرەوە و @staticmethod بۆ یارمەتیدەر بەکار بهێنیت و جۆری گونجاوی هەڵبژێریت.
  • بچووک بخوێنیتەوە و فراوانی بکەیت کە تێیدا چەند پێکەوە کار دەکەن.

What an object knows and what it can doئۆبجێکتێک چی دەزانێت و دەتوانێت چی بکات

An object knows things, its attributes, and can do things, its methods. The last lesson, Classes and objects, made both with self. This lesson looks closer: attributes that belong to each object or to the whole class, methods that change an object or answer a question, the special methods Python calls by itself, and two more kinds of methods. All examples come from one small school with students, teachers and courses.

شت دەزانێت (ئەمانە ) و دەتوانێت شت بکات (ئەمانە ). وانەی پێشوو، کلاسەکان و ئۆبجێکتەکان، هەردووکیانی بە self دروست کرد. ئەم وانەیە نزیکتر سەیر دەکات: ئەو هی هەر یان هی هەموو ، ئەو دەگۆڕن یان وەڵامی پرسیارێک دەدەنەوە، ئەو خۆی بانگیان دەکات () و دوو جۆری تری . هەموو نموونەکان لە قوتابخانەیەکی بچووکەوە دێن کە قوتابی، مامۆستا و کۆرسی هەیە.

A class box for class Student with three parts. Top: the class attributes school = "Azadi School" and pass_mark = 50, one copy shared by all objects. Middle: the instance attributes self.name and self.marks, which each object has for itself. Bottom: the methods, each with a coloured square: __init__(self, name) and __str__(self) are special methods, add_mark(self, mark) and average(self) are instance methods with self, from_line(cls, line) is a class method with cls, and is_valid_mark(mark) is a static method with no self and no cls.چوارگۆشەی کلاسی Student بە سێ بەش. سەرەوە: خەسڵەتەکانی کلاسی school = "Azadi School" و pass_mark = 50، یەک کۆپی کە هەموو ئۆبجێکتەکان بەشی دەکەن. ناوەڕاست: خەسڵەتەکانی نموونەی self.name و self.marks کە هەر ئۆبجێکتێک هی خۆی هەیە. خوارەوە: میتۆدەکان، هەریەکەیان بە چوارگۆشەیەکی ڕەنگاوڕەنگ: __init__(self, name) و __str__(self) میتۆدی تایبەتن، add_mark(self, mark) و average(self) میتۆدی نموونەن بە self، from_line(cls, line) میتۆدی کلاسە بە cls و is_valid_mark(mark) میتۆدی ستاتیکە بێ self و بێ cls.
Everything a class can hold, and the four kinds of methods in this lesson.هەموو ئەو شتانەی دەتوانێت هەڵیانبگرێت و چوار جۆرەکەی لەم وانەیەدا.CêIT

Instance attributes and class attributesخەسڵەتەکانی نموونە و خەسڵەتەکانی کلاس

An attribute written in the class body, outside every method, is a class attribute: it belongs to the class, and all its objects share one copy. Attributes made with self. are instance attributes: each object has its own.

کە لە لاشەی ، لە دەرەوەی هەموو ، نووسراوە : هی و هەموو یەک کۆپی بەش دەکەن. ئەو بە self. دروست دەکرێن ن: هەر هی خۆی هەیە.

A school name for every studentناوی قوتابخانە بۆ هەموو قوتابییەکclass_vs_instance.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    school = "Azadi School"          # class attribute: one copy for all

    def __init__(self, name, grade):
        self.name = name             # instance attributes: one per object
        self.grade = grade

aram = Student("Aram", 10)
shilan = Student("Shilan", 11)

print(aram.school)
print(shilan.school)
print(Student.school)
print(vars(aram))                    # school is not stored in the object
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

Written once, in the class body: a class attribute.

یەک جار لە لاشەی نووسراوە: .

school is not in vars(aram), yet aram.school works. When you read aram.school, Python looks in the object first and, if the name is not there, in its class. The animation shows the search, and what happens when neither has the name.

school لە vars(aram)ـدا نییە، بەڵام aram.school کار دەکات. کاتێک aram.school دەخوێنیتەوە، سەرەتا لە دەگەڕێت و ئەگەر ناوەکە لەوێ نەبوو، لە . ئەنیمەیشنەکە گەڕانەکە پیشان دەدات و ئەوەی ڕوو دەدات کاتێک هیچیان ناوەکەیان نییە.

Animation in four frames. Left: the object aram with name "Aram" and grade 10, labelled look here first. Right: the class Student with school "Azadi School" and count 2, labelled then here. Frame 1: aram.name is found in the object. Frame 2: aram.school is not in the object, so Python looks in the class. Frame 3: it finds school in the class. Frame 4: aram.mark is in neither, and the result is AttributeError.
Animation in four frames. Left: the object aram with name "Aram" and grade 10, labelled look here first. Right: the class Student with school "Azadi School" and count 2, labelled then here. Frame 1: aram.name is found in the object. Frame 2: aram.school is not in the object, so Python looks in the class. Frame 3: it finds school in the class. Frame 4: aram.mark is in neither, and the result is AttributeError.
Python looks in the object first, then in its class. سەرەتا لە دەگەڕێت، پاشان لە .CêIT

Instance attributes and class attributesخەسڵەتەکانی نموونە و خەسڵەتەکانی کلاس

Change a class attribute through the classخەسڵەتێکی کلاس لە ڕێگەی کلاسەکەوە بگۆڕە

Change a class attribute through the class, and every object sees the new value. Assigning through an object does something else: it makes a new instance attribute with the same name, which hides the class attribute for that object only:

لە ڕێگەی بگۆڕە و هەموو نوێیەکە دەبینێت. دانانی لە ڕێگەی کارێکی تر دەکات: نوێ بە هەمان ناو دروست دەکات کە تەنیا بۆ ئەو دەشارێتەوە:

Through the class, then through one objectلە ڕێگەی کلاسەکەوە، پاشان لە ڕێگەی یەک ئۆبجێکتەوەchange_class_attr.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    school = "Azadi School"

    def __init__(self, name):
        self.name = name

aram = Student("Aram")
shilan = Student("Shilan")

Student.school = "Azadi High School"     # change it on the class
print(aram.school, "|", shilan.school)

aram.school = "Rozh School"              # makes a NEW instance attribute
print(aram.school, "|", shilan.school, "|", Student.school)
print(vars(aram))

del aram.school                          # remove it: the class value shows again
print(aram.school)

After aram.school = "Rozh School", Aram has his own school while the others still read the class's value. del aram.school removed Aram's copy, so the class value showed again. This hiding is called shadowing. Read class attributes through objects if you like, but change them through the class.

دوای aram.school = "Rozh School"، ئارام schoolـی خۆی هەیە و ئەوانی تر هێشتا دەخوێننەوە. del aram.school کۆپییەکەی ئارامی لابرد، بۆیە دووپاتە دەرکەوتەوە. بەم شاردنەوەیە دەگوترێت . ئەگەر دەتەوێت لە ڕێگەی بخوێنەوە، بەڵام لە ڕێگەی بیانگۆڕە.

The class Student holds school "Azadi High School". The object aram has name "Aram" and its own school "Rozh School", highlighted; its dashed arrow to the class is crossed out. The object shilan has only name "Shilan", and a solid arrow leads to the class. Labels: aram.school gives "Rozh School" (its own); shilan.school gives "Azadi High School" (from the class).کلاسی Student school "Azadi High School" هەڵدەگرێت. ئۆبجێکتی aram name "Aram" و school "Rozh School"ـی خۆی هەیە بە ڕەنگی جیاواز؛ تیرە پچڕپچڕەکەی بۆ کلاسەکە خەتی بەسەردا هاتووە. ئۆبجێکتی shilan تەنیا name "Shilan"ـی هەیە و تیرێکی پڕ دەچێت بۆ کلاسەکە. ناونیشانەکان: aram.school دەداتە "Rozh School" (هی خۆی)؛ shilan.school دەداتە "Azadi High School" (لە کلاسەکەوە).
An instance attribute with the same name hides the class attribute for that one object. بە هەمان ناو تەنیا بۆ ئەو یەک دەشارێتەوە.CêIT

Instance attributes and class attributesخەسڵەتەکانی نموونە و خەسڵەتەکانی کلاس

A class attribute as a counterخەسڵەتێکی کلاس وەک ژمێرەر

A class attribute is the place for facts about the whole class, such as how many students exist. __init__ adds one to Student.count each time an object is made:

شوێنی ئەو زانیاریانەیە کە لەبارەی هەموو ، وەک ئەوەی چەند قوتابی هەیە. __init__ هەر جارێک دروست دەکرێت یەک بۆ Student.count زیاد دەکات:

Counting the studentsژماردنی قوتابییەکانcounter.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    count = 0                        # how many Student objects exist

    def __init__(self, name):
        self.name = name
        Student.count += 1           # change the CLASS attribute

aram = Student("Aram")
shilan = Student("Shilan")
karwan = Student("Karwan")
print(Student.count, aram.count)

Write Student.count += 1, not self.count += 1. The second reads 0 from the class and then assigns to self.count, which makes an instance attribute in every object, while the class counter stays at 0:

Student.count += 1 بنووسە، نەک self.count += 1. دووەمیان 0 لە دەخوێنێتەوە و پاشان دادەنێت بۆ self.count، کە لە هەموو دروست دەکات، لە کاتێکدا لە 0 دەمێنێتەوە:

The mistake: self.count += 1هەڵەکە: ⁦self.count += 1⁩counter_mistake.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    count = 0

    def __init__(self, name):
        self.name = name
        self.count += 1              # reads 0 from the class, writes a NEW attribute

aram = Student("Aram")
shilan = Student("Shilan")
print(Student.count, aram.count, shilan.count)
Two panels. Right way, Student.count += 1: the class Student shows count 3, and the objects aram, shilan and karwan have no count of their own; arrows lead from them to the class. Wrong way, self.count += 1: the class still shows count 0, and each object has its own count 1.دوو بەش. شێوەی ڕاست، Student.count += 1: کلاسی Student count 3 پیشان دەدات و ئۆبجێکتەکانی aram، shilan و karwan countـی خۆیان نییە؛ تیرەکان لەوانەوە دەچن بۆ کلاسەکە. شێوەی هەڵە، self.count += 1: کلاسەکە هێشتا count 0 پیشان دەدات و هەر ئۆبجێکتێک count 1ـی خۆی هەیە.
Change the class attribute through the class; self.count += 1 makes one counter per object. لە ڕێگەی بگۆڕە؛ self.count += 1 بۆ هەر دروست دەکات.CêIT

Instance attributes and class attributesخەسڵەتەکانی نموونە و خەسڵەتەکانی کلاس

The shared list trapتەڵەی لیستی هاوبەش

A class attribute that is a list (or a dictionary) is one list for all objects. self.marks.append(...) does not assign anything; it changes that one shared list, so every student gets everybody's marks:

کە (یان ) یەک لیستە بۆ هەموو . self.marks.append(...) هیچ دانانێت؛ ئەو یەک لیستە هاوبەشە دەگۆڕێت، بۆیە هەموو قوتابییەک نمرەی هەمووان وەردەگرێت:

One list in the class: wrongیەک لیست لە کلاسەکەدا: هەڵەshared_list_trap.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    marks = []                       # WRONG: one list for every student

    def __init__(self, name):
        self.name = name

    def add_mark(self, mark):
        self.marks.append(mark)

aram = Student("Aram")
shilan = Student("Shilan")
aram.add_mark(90)
shilan.add_mark(55)
print(aram.marks)
print(shilan.marks)
print(aram.marks is shilan.marks)

The fix: make the list in __init__, so every object gets a new one. This is the mutable trap you met with lists in functions and in the Python tutorial:

چارەسەرەکە: لیستەکە لە __init__ـدا دروست بکە، بۆ ئەوەی هەر لیستێکی نوێ وەربگرێت. ئەمە هەمان تەڵەیە کە لە لیستەکان لە کردارەکاندا و لە فێرکاریی Python بینیت، چونکە گۆڕانهەڵگرە:

A new list for each student: rightلیستێکی نوێ بۆ هەر قوتابییەک: ڕاستshared_list_fixed.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name):
        self.name = name
        self.marks = []              # RIGHT: a new list for each student

    def add_mark(self, mark):
        self.marks.append(mark)

aram = Student("Aram")
shilan = Student("Shilan")
aram.add_mark(90)
shilan.add_mark(55)
print(aram.marks)
print(shilan.marks)
print(aram.marks is shilan.marks)
Two panels. Wrong, marks = [] in the class: the names aram and shilan both point to one list [90, 55]: one list for everybody; aram.marks is shilan.marks gives True. Right, self.marks = [] in __init__: aram points to [90] and shilan to [55]: a new list for each student; aram.marks is shilan.marks gives False.دوو بەش. هەڵە، marks = [] لە کلاسەکەدا: ناوەکانی aram و shilan هەردووکیان ئاماژە بۆ یەک لیستی [90, 55] دەکەن: یەک لیست بۆ هەمووان؛ aram.marks is shilan.marks دەداتە True. ڕاست، self.marks = [] لە __init__ـدا: aram ئاماژە بۆ [90] و shilan بۆ [55] دەکات: لیستێکی نوێ بۆ هەر قوتابییەک؛ aram.marks is shilan.marks دەداتە False.
A list in the class body is shared by every object; a list made in __init__ belongs to one object.لیستێک لە لاشەی هاوبەشی هەموو ؛ لیستێک کە لە __init__ـدا دروست دەکرێت هی یەک .CêIT

Instance methods: actions on one objectمیتۆدەکانی نموونە: کار لەسەر یەک ئۆبجێکت

An instance method is an ordinary method with self first. Extra parameters come after self, and the call fills them from left to right: in aram.add_mark(72), self is aram and mark is 72. Inside, self. reaches this object's attributes:

ئاساییە بە self لە پێشەوە. زیاتر دوای self دێن و بانگکردنەکە لە چەپەوە بۆ ڕاست پڕیان دەکاتەوە: لە aram.add_mark(72)ـدا، self aramـە و mark 72ـە. لە ناوەوە، self. دەگاتە ئەم :

add_mark() changes, average() answers⁦add_mark()⁩ دەگۆڕێت، ⁦average()⁩ وەڵام دەداتەوەmethods_basic.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name):
        self.name = name
        self.marks = []

    def add_mark(self, mark):
        self.marks.append(mark)

    def average(self):
        return sum(self.marks) / len(self.marks)

aram = Student("Aram")
aram.add_mark(72)
aram.add_mark(48)
aram.add_mark(90)
print(aram.marks)
print(aram.average())

add_mark() changes the object: it appends to the object's own list. average() reads the object and returns a number. These two kinds of methods, changing and answering, appear in almost every class.

add_mark() دەگۆڕێت: بۆ لیستی تایبەتی زیاد دەکات. average() دەخوێنێتەوە و ژمارەیەک دەگەڕێنێتەوە. ئەم دوو جۆرە ، گۆڕین و وەڵامدانەوە، لە نزیکەی هەموو دەردەکەون.

The call aram.add_mark(72) at the top. Two arrows lead down into def add_mark(self, mark): with the body self.marks.append(mark): aram goes into self and 72 goes into mark. Below, the card aram: Student shows marks [72], highlighted: the object's own list grows.بانگکردنی aram.add_mark(72) لە سەرەوە. دوو تیر دەچنە خوارەوە بۆ ناو def add_mark(self, mark): بە لاشەی self.marks.append(mark): aram دەچێتە ناو self و 72 دەچێتە ناو mark. لە خوارەوە کارتی aram: Student marks [72] بە ڕەنگی جیاواز پیشان دەدات: لیستی تایبەتی ئۆبجێکتەکە گەورە دەبێت.
The object before the dot fills self; the argument in the brackets fills mark.ئەو پێش خاڵەکەیە self پڕ دەکاتەوە؛ ئەو لە ناو کەوانەکەدایە mark پڕ دەکاتەوە.CêIT

Instance methods: actions on one objectمیتۆدەکانی نموونە: کار لەسەر یەک ئۆبجێکت

Methods that guard the objectئەو میتۆدانەی ئۆبجێکتەکە دەپارێزن

The values of an object's attributes at one moment are its state. A method that changes the state is a good place to check the new value first. If it is wrong, raise an exception, so the object never holds a bad value:

لە هەر ساتێکدا، دۆخی ئەو پێک دەهێنن. کە دۆخەکە دەگۆڕێت شوێنێکی باشە بۆ ئەوەی سەرەتا نوێیەکە بپشکنێت. ئەگەر هەڵە بوو، هەڵاوێردەیەک دروست بکە، بۆ ئەوەی هەرگیز خراپ هەڵنەگرێت:

add_mark() refuses a wrong mark⁦add_mark()⁩ نمرەی هەڵە ڕەت دەکاتەوەmethods_validation.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name):
        self.name = name
        self.marks = []

    def add_mark(self, mark):
        if not 0 <= mark <= 100:     # protect the object's state
            raise ValueError(f"a mark must be 0-100, not {mark}")
        self.marks.append(mark)

aram = Student("Aram")
aram.add_mark(72)
print(aram.marks)
aram.add_mark(720)                   # a typing slip: 720 instead of 72
print(aram.marks)
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 7-8

Check first, change after: a wrong mark stops here and is never appended.

سەرەتا بپشکنە، پاشان بگۆڕە: نمرەی هەڵە لێرە دەوەستێت و هەرگیز زیاد ناکرێت.

The typing slip 720 was refused with a ValueError, and Aram's marks stayed [72]. Other code could still write aram.marks.append(720) directly, because Python trusts programmers; how to protect attributes better is in the next lesson, Encapsulation and properties.

هەڵە نووسینەکەی ⁦720⁩ بە ValueError ڕەت کرایەوە و نمرەکانی ئارام لە [72] مانەوە. کۆدی تر هێشتا دەتوانێت ڕاستەوخۆ aram.marks.append(720) بنووسێت، چونکە متمانە بە دەکات؛ چۆن باشتر بپارێزیت لە وانەی داهاتوودایە: کەپسوولکردن و تایبەتمەندییەکان.

Three boxes show aram.marks over time: [] then, after add_mark(72), [72]. A dashed arrow marked add_mark(720) leads to a ValueError: 0-100 box, and the last box still shows [72]: state unchanged. Bottom: a bad value is refused, so the object never holds it.سێ چوارگۆشە aram.marks بە درێژایی کات پیشان دەدەن: [] پاشان، دوای add_mark(72)، [72]. تیرێکی پچڕپچڕ بە ناوی add_mark(720) دەچێت بۆ چوارگۆشەی ValueError: 0-100 و دوا چوارگۆشە هێشتا [72] پیشان دەدات: دۆخەکە نەگۆڕاوە. خوارەوە: نرخێکی هەڵە ڕەت دەکرێتەوە، بۆیە ئۆبجێکتەکە هەرگیز هەڵی ناگرێت.
A method that checks before it changes keeps the object's state valid. کە پێش گۆڕین دەپشکنێت دۆخی دروست دەهێڵێتەوە.CêIT

Instance methods: actions on one objectمیتۆدەکانی نموونە: کار لەسەر یەک ئۆبجێکت

Methods that return a valueئەو میتۆدانەی نرخێک دەگەڕێننەوە

A method that answers a question should return the answer, so the caller can print it, compare it or use it in another calculation. A method can call another method of the same object through self:

کە وەڵامی پرسیارێک دەداتەوە دەبێت وەڵامەکە بگەڕێنێتەوە، بۆ ئەوەی بتوانێت چاپی بکات، بەراوردی بکات یان لە ژماردنێکی تردا بەکاری بهێنێت. دەتوانێت لە ڕێگەی selfـەوە تری هەمان بانگ بکات:

average(), has_passed() and a report that only prints⁦average()⁩، ⁦has_passed()⁩ و ڕاپۆرتێک کە تەنیا چاپ دەکاتmethods_return.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    pass_mark = 50

    def __init__(self, name):
        self.name = name
        self.marks = []

    def add_mark(self, mark):
        self.marks.append(mark)

    def average(self):
        if not self.marks:                    # no marks yet: avoid dividing by 0
            return 0.0
        return sum(self.marks) / len(self.marks)

    def has_passed(self):
        return self.average() >= self.pass_mark   # a method using a method

    def report(self):
        print(f"{self.name}: {self.average():.1f}")   # prints, returns None

aram = Student("Aram")
print(aram.average(), aram.has_passed())
for m in [72, 48, 90]:
    aram.add_mark(m)
print(aram.average(), aram.has_passed())
result = aram.report()
print(result)
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 12-13

No marks yet: return 0.0 instead of dividing by zero.

هێشتا نمرە نییە: لەبری دابەشکردن بەسەر سفردا ⁦0.0⁩ بگەڕێنەوە.

Lineدێڕی 17

self.average() calls another method of the same object.

self.average() تری هەمان بانگ دەکات.

report() only prints, so it returns None: fine for a report, wrong for a value you need later (return or print?). has_passed() reads the class attribute pass_mark through self, which finds it in the class.

report() تەنیا چاپ دەکات، بۆیە None دەگەڕێنێتەوە: بۆ ڕاپۆرت باشە، بەڵام بۆ کە دواتر پێویستت دەبێت هەڵەیە (return یان print؟). has_passed() pass_mark لە ڕێگەی selfـەوە دەخوێنێتەوە کە لە دەیدۆزێتەوە.

Two panels. Left: x = aram.average(); return hands 70.0 back, and the caller can use it in x >= 50, round(x) or print(x). Right: r = aram.report() shows Aram: 70.0 on the screen, but r is None: only text on the screen, and the caller gets None.دوو بەش. چەپ: x = aram.average()؛ return 70.0 دەگەڕێنێتەوە و بانگکەرەکە دەتوانێت لە x >= 50، round(x) یان print(x)ـدا بەکاری بهێنێت. ڕاست: r = aram.report() Aram: 70.0 لەسەر شاشە پیشان دەدات، بەڵام r is None: تەنیا دەق لەسەر شاشە و بانگکەرەکە None وەردەگرێت.
Return a value the caller can use; print only shows text. بگەڕێنەوە کە بتوانێت بەکاری بهێنێت؛ print تەنیا دەق پیشان دەدات.CêIT

__str__ and __repr__: how an object shows itself__str__ و __repr__: ئۆبجێکتێک چۆن خۆی پیشان دەدات

Two special methods turn an object into text. __str__ gives a friendly text for people: print(), str() and f-strings use it. __repr__ gives an exact text for programmers: repr(), the Python shell and lists of objects use it. A good __repr__ looks like the code that makes the object:

دوو دەکەنە دەق. __str__ دەقێکی ڕوون بۆ خەڵک دەدات: print()، str() و بەکاری دەهێنن. __repr__ دەقێکی ورد بۆ دەدات: repr()، شێڵی و بەکاری دەهێنن. __repr__ـێکی باش وەک ئەو کۆدە دەردەکەوێت کە دروست دەکات:

A friendly text and an exact textدەقێکی ڕوون و دەقێکی وردstr_repr.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __str__(self):               # for people: print(), str(), f-strings
        return f"{self.name} (grade {self.grade})"

    def __repr__(self):              # for programmers: repr(), lists, the shell
        return f"Student({self.name!r}, {self.grade})"

aram = Student("Aram", 10)
print(aram)
print(f"Top student: {aram}")
print(repr(aram))
print([aram, Student("Dilan", 12)])
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 10

!r writes the value with repr(), so the name keeps its quotes: 'Aram'.

!r بە repr() دەنووسێت، بۆیە ناوەکە کەوانەکانی دەمێنێت: 'Aram'.

A list shows its items with __repr__, which is why the last line shows Student('Aram', 10). If a class has only __repr__, print() uses it as well, so when you write only one, write __repr__. Both must return a string.

دانەکانی بە __repr__ پیشان دەدات، بۆیە دوا دێڕ Student('Aram', 10) پیشان دەدات. ئەگەر تەنیا __repr__ـی هەبێت، print()ـیش ئەوە بەکار دەهێنێت، بۆیە ئەگەر تەنیا یەکێکیان دەنووسیت، __repr__ بنووسە. هەردووکیان دەبێت بگەڕێننەوە.

Two columns. __str__, for people: print(aram), str(aram) and f"{aram}" all give Aram (grade 10). __repr__, for programmers: repr(aram), the list [aram, dilan] and >>> aram in the shell all give Student('Aram', 10). Bottom: only __repr__ written? print() uses it too. Both must return a string.دوو ستوون. __str__، بۆ خەڵک: print(aram)، str(aram) و f"{aram}" هەموویان Aram (grade 10) دەدەن. __repr__، بۆ بەرنامەسازەکان: repr(aram)، لیستی [aram, dilan] و >>> aram لە شێڵدا هەموویان Student('Aram', 10) دەدەن. خوارەوە: تەنیا __repr__ نووسراوە؟ print()ـیش ئەوە بەکار دەهێنێت. هەردووکیان دەبێت زنجیرەنووسەیەک بگەڕێننەوە.
Which calls use __str__ and which use __repr__.کام بانگکردنەکان __str__ بەکار دەهێنن و کامیان __repr__.CêIT

Comparing objects: == and __eq__بەراوردکردنی ئۆبجێکتەکان: == و __eq__

Without help, == between two of your objects asks the same as is: are they one object? So two objects made from the same typed-in data are not equal (as in the last lesson). Teach the class what equal means with __eq__. Here two students are equal when their student numbers match:

بەبێ یارمەتی، == لە نێوان دوو خۆتدا هەمان پرسیاری is دەکات: ئایا یەک ؟ بۆیە دوو کە لە هەمان نووسراو دروست کراون یەکسان نین (وەک وانەی پێشوو). بە __eq__ فێری بکە یەکسان واتای چییە. لێرەدا دوو قوتابی یەکسانن کاتێک ژمارەی قوتابییان وەک یەک بێت:

Two students are equal when their numbers matchدوو قوتابی یەکسانن کاتێک ژمارەکانیان وەک یەک بێتeq.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, student_id, name):
        self.student_id = student_id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, Student):
            return NotImplemented    # "I cannot compare with that"
        return self.student_id == other.student_id

a = Student(1021, "Aram")
b = Student(1021, "Aram")            # the same student, typed in twice
c = Student(1034, "Shilan")

print(a == b, a is b)
print(a == c, a != c)
print(a == "Aram")
print(b in [c, a])                   # in uses == too
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 7-8

For other types return NotImplemented: Python then tries the other side and finally answers False.

بۆ جۆرەکانی تر NotImplemented بگەڕێنەوە: پاشان لایەکەی تر تاقی دەکاتەوە و لە کۆتاییدا False دەڵێت.

a == b is now True while a is b stays False: equal, but two objects. != and in use __eq__ too, so b in [c, a] finds Aram.

ئێستا a == b Trueـە، لە کاتێکدا a is b هێشتا Falseـە: یەکسانن، بەڵام دوو . != و inـیش __eq__ بەکار دەهێنن، بۆیە b in [c, a] ئارام دەدۆزێتەوە.

Three cards: a and b both have student_id 1021 and name "Aram"; c has student_id 1034 and name "Shilan". Below: a == b gives True (same student number, __eq__); a is b gives False (two separate objects); a == c gives False (different numbers).سێ کارت: a و b هەردووکیان student_id 1021 و name "Aram"یان هەیە؛ c student_id 1034 و name "Shilan"ـی هەیە. لە خوارەوە: a == b دەداتە True (هەمان ژمارەی قوتابی، __eq__)؛ a is b دەداتە False (دوو ئۆبجێکتی جیاواز)؛ a == c دەداتە False (ژمارەی جیاواز).
== asks "equal?" through __eq__; is asks "the same object?".== لە ڕێگەی __eq__ـەوە دەپرسێت «یەکسانن؟»؛ is دەپرسێت «هەمان ؟».CêIT

Comparing objects: == and __eq__بەراوردکردنی ئۆبجێکتەکان: == و __eq__

Equal objects in sets and dictionariesئۆبجێکتی یەکسان لە کۆمەڵەکان و فەرهەنگەکاندا

Sets and dictionary keys find items by a hash number. A class that defines __eq__ but no __hash__ cannot be put in a set at all:

و دانەکان بە ژمارەیەکی هاش دەدۆزنەوە. کە __eq__ پێناسە دەکات، بەڵام __hash__ نا، هەرگیز ناتوانرێت بخرێتە ناو :

__eq__ without __hash____eq__ بێ __hash__eq_hash.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, student_id, name):
        self.student_id = student_id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, Student):
            return NotImplemented
        return self.student_id == other.student_id

a = Student(1021, "Aram")
b = Student(1021, "Aram")
print({a, b})

Python sets __hash__ to None in such a class, because equal objects must have equal hashes. Give the class a __hash__ built from the same value that __eq__ compares:

لەم جۆرە __hash__ دەکاتە None، چونکە یەکسان دەبێت هاشی یەکسانیان هەبێت. __hash__ـێک بدە بە کە لە هەمان دروست بکرێت کە __eq__ بەراوردی دەکات:

A matching __hash____hash__ـێکی گونجاوeq_hash_fixed.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, student_id, name):
        self.student_id = student_id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, Student):
            return NotImplemented
        return self.student_id == other.student_id

    def __hash__(self):              # equal students give the same hash
        return hash(self.student_id)

a = Student(1021, "Aram")
b = Student(1021, "Aram")
c = Student(1034, "Shilan")
print(len({a, b, c}))
seats = {a: "row 1"}
print(seats[b])                      # b finds a's seat: they are equal

Aram is counted once in the set, and b finds the seat stored under a. Hash only values that never change: if a student's number could change, leave __hash__ out. More: what can be a key?

ئارام یەک جار لە دەژمێردرێت و b ئەو کورسییە دەدۆزێتەوە کە بە a هەڵگیراوە. تەنیا ئەو هاش بکە کە هەرگیز ناگۆڕێن: ئەگەر ژمارەی قوتابییەک بتوانێت بگۆڕێت، __hash__ مەنووسە. زیاتر: چی دەتوانێت ببێتە کلیل؟

Special methods: Python calls them for youمیتۆدە تایبەتەکان: Python بۆت بانگیان دەکات

Names with two underscores on each side are special methods, also called dunder methods (from double underscore). You write them, and Python calls them when your object meets an operator or a built-in function. Course below works with len() and print():

ئەو ناوانەی دوو هێڵی ژێرەوەیان لە هەر لایەکەوە هەیە ن کە پێیان دەگوترێت dunder (double underscore). تۆ دەیاننووسیت و کاتێک بەر یان دەکەوێت بانگیان دەکات. Courseـی خوارەوە لەگەڵ len() و print() کار دەکات:

A course that knows its lengthکۆرسێک کە درێژیی خۆی دەزانێتspecial_methods.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Course:
    def __init__(self, title):       # Course("Python 1")
        self.title = title
        self.students = []

    def enrol(self, name):
        self.students.append(name)

    def __len__(self):               # len(course)
        return len(self.students)

    def __str__(self):               # print(course), str(course)
        return f"{self.title}: {len(self)} students"

python = Course("Python 1")
python.enrol("Aram")
python.enrol("Hana")
print(len(python))
print(python)
print(python.__len__())              # what len() really calls
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 20

len(python) is really python.__len__().

len(python) لە ڕاستیدا python.__len__()ـە.

There are special methods for operators too, such as __add__ for + and __lt__ for <; the data model lists them all. PEP 8 asks you never to invent new names of this shape: use only those Python documents.

بۆ هەن، وەک __add__ بۆ + و __lt__ بۆ <؛ بەڵگەنامەکانی Python هەموویانی لیست کردووە. ⁦PEP 8⁩ داوات لێ دەکات هەرگیز ناوی نوێی بەم شێوەیە دامەهێنیت: تەنیا ئەوانە بەکار بهێنە کە ناساندوونی.

Six rows, each with code on the left and an arrow to a special method on the right: Student("Aram", 10) calls __init__; print(x), str(x) and f"{x}" call __str__; repr(x) and a list [x, y] call __repr__; x == y, x != y and x in a_list call __eq__; len(x) calls __len__; {x} and d[x] call __hash__. Bottom: use only the names Python documents.شەش ڕیز، هەریەکەیان کۆدێک لە چەپ و تیرێک بۆ میتۆدێکی تایبەت لە ڕاست: Student("Aram", 10) __init__ بانگ دەکات؛ print(x)، str(x) و f"{x}" __str__ بانگ دەکەن؛ repr(x) و لیستی [x, y] __repr__ بانگ دەکەن؛ x == y، x != y و x in a_list __eq__ بانگ دەکەن؛ len(x) __len__ بانگ دەکات؛ {x} و d[x] __hash__ بانگ دەکەن. خوارەوە: تەنیا ئەو ناوانە بەکار بهێنە کە Python ناساندوونی.
You write the left side; Python calls the special method on the right.تۆ لای چەپ دەنووسیت؛ لای ڕاست بانگ دەکات.CêIT

Class methods: @classmethod and clsمیتۆدەکانی کلاس: @classmethod و cls

Sometimes a method needs the class, not one object, most often to make objects in a second way. Mark it with the decorator @classmethod: a line starting with @ above def that changes how the method is called. Python then passes the class as the first parameter, named cls by convention. A class method that builds a student from a line of text is an alternative constructor:

هەندێک جار پێویستی بە هەیە، نەک بە یەک ، زۆربەی جار بۆ دروستکردنی بە ڕێگایەکی تر. بە @classmethod نیشانەی بکە: دێڕێک کە بە @ دەست پێ دەکات و لە سەرووی defـەوە دەنووسرێت و شێوەی بانگکردنی دەگۆڕێت. ئەو کاتە وەک یەکەم دەنێرێت کە بە ڕێککەوتن ناوی clsـە. کە قوتابییەک لە دێڕێکی دەق دروست دەکات جێگرەوەیە:

Students from lines of textقوتابی لە دێڕی دەقەوەclassmethod_demo.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    @classmethod
    def from_line(cls, line):        # an alternative constructor
        name, grade = line.split(",")
        return cls(name.strip(), int(grade))

    def __repr__(self):
        return f"Student({self.name!r}, {self.grade})"

rows = ["Aram, 10", "Shilan, 11", "Karwan, 10"]   # lines from a text file
students = []
for row in rows:
    students.append(Student.from_line(row))
print(students)
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 6-9

cls(...) is the same as Student(...) here: it calls the class, and so __init__.

cls(...) لێرەدا هەمان Student(...)ـە: بانگ دەکات و بەمەش __init__.

Lineدێڕی 14

Lines like these come from a text file.

دێڕی وەک ئەمانە لە فایلێکی دەقییەوە دێن.

You call a class method on the class: Student.from_line(row). It returns a ready object, so the code that splits text stays out of __init__, and __init__ stays simple.

لەسەر خودی بانگ دەکەیت: Student.from_line(row). ئامادە دەگەڕێنێتەوە، بۆیە ئەو کۆدەی دەق دابەش دەکات لە دەرەوەی __init__ دەمێنێتەوە و __init__ سادە دەمێنێتەوە.

Three lines of text, "Aram, 10", "Shilan, 11" and "Karwan, 10", go into Student.from_line(line), where cls is Student and the method returns cls("Aram", 10). Arrows lead to three Student objects: Aram, Shilan and Karwan. Bottom: an alternative constructor, another way to make Student objects.سێ دێڕی دەق، "Aram, 10"، "Shilan, 11" و "Karwan, 10"، دەچنە ناو Student.from_line(line) کە تێیدا cls هەمان Studentـە و میتۆدەکە cls("Aram", 10) دەگەڕێنێتەوە. تیرەکان دەچن بۆ سێ ئۆبجێکتی Student: ئارام، شیلان و کاروان. خوارەوە: دروستکەرێکی جێگرەوە، ڕێگایەکی تر بۆ دروستکردنی ئۆبجێکتەکانی Student.
A class method receives the class as cls and can make objects with it. وەک cls وەردەگرێت و دەتوانێت پێ دروست بکات.CêIT

Static methods: @staticmethodمیتۆدی ستاتیک: @staticmethod

A static method, marked @staticmethod, gets no automatic first parameter at all: no self and no cls. It is a plain helper function kept inside the class because it belongs to the class's topic, such as the rule for a valid mark. This class uses all three kinds:

ستاتیک، کە بە @staticmethod نیشانە دەکرێت، هیچ یەکەمی خۆکار وەرناگرێت: نە self و نە cls. یارمەتیدەری سادەیە کە لە ناو هێڵراوەتەوە چونکە سەر بە بابەتی ، وەک یاسای نمرەی دروست. ئەم هەر سێ جۆرەکە بەکار دەهێنێت:

An instance method, a class method and a static methodمیتۆدی نموونە، میتۆدی کلاس و میتۆدی ستاتیکthree_kinds.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    pass_mark = 50                   # class attribute

    def __init__(self, name, mark):
        if not Student.is_valid_mark(mark):
            raise ValueError(f"bad mark: {mark}")
        self.name = name
        self.mark = mark

    def has_passed(self):            # instance method: gets the object
        return self.mark >= self.pass_mark

    @classmethod
    def set_pass_mark(cls, mark):    # class method: gets the class
        cls.pass_mark = mark

    @staticmethod
    def is_valid_mark(mark):         # static method: gets nothing extra
        return isinstance(mark, int) and 0 <= mark <= 100

print(Student.is_valid_mark(85), Student.is_valid_mark(120))
aram = Student("Aram", 55)
print(aram.has_passed())
Student.set_pass_mark(60)            # a new rule for every student
print(aram.has_passed(), Student.pass_mark)

set_pass_mark() changes the class attribute through cls, so the new rule reaches every student at once: Aram's 55 is no longer a pass. In Java, one keyword, static, covers members that belong to the class: static members in Java.

set_pass_mark() لە ڕێگەی clsـەوە دەگۆڕێت، بۆیە یاسا نوێیەکە یەکسەر دەگاتە هەموو قوتابییەک: ⁦55⁩ـی ئارام چیتر دەرچوون نییە. لە یەک ، static، ئەو ئەندامانە دەگرێتەوە کە هی : ئەندامە ستاتیکەکان لە Java.

Three columns. Instance method: aram.has_passed() receives self = aram, the object; def has_passed(self):. Class method: Student.set_pass_mark(60) receives cls = Student, the class; @classmethod above def set_pass_mark(cls, mark):. Static method: Student.is_valid_mark(85) receives nothing extra; @staticmethod above def is_valid_mark(mark):.سێ ستوون. میتۆدی نموونە: aram.has_passed() self = aram وەردەگرێت، واتە ئۆبجێکتەکە؛ def has_passed(self):. میتۆدی کلاس: Student.set_pass_mark(60) cls = Student وەردەگرێت، واتە کلاسەکە؛ @classmethod لە سەرووی def set_pass_mark(cls, mark):. میتۆدی ستاتیک: Student.is_valid_mark(85) هیچی زیادە وەرناگرێت؛ @staticmethod لە سەرووی def is_valid_mark(mark):.
What each kind of method receives first.هەر جۆرێکی سەرەتا چی وەردەگرێت.CêIT

Static methods: @staticmethodمیتۆدی ستاتیک: @staticmethod

Which kind of method?کام جۆری میتۆد؟

Choose by what the method needs:

بەپێی ئەوەی پێویستی پێیەتی هەڵبژێرە:

The three kinds of methods in a Python class.سێ جۆرەکەی لە .
KindجۆرFirst parameterیەکەم Called asبانگکردنUse it whenکەی بەکاری بهێنیت
Instance method selfaram.has_passed()it reads or changes one object's attributes (most methods) یەک دەخوێنێتەوە یان دەیانگۆڕێت (زۆربەی )
Class method, @classmethod ، @classmethodclsStudent.from_line(...)it makes objects in another way or changes class attributesبە ڕێگایەکی تر دروست دەکات یان دەگۆڕێت
Static method, @staticmethod ستاتیک، @staticmethodnoneهیچStudent.is_valid_mark(85)it needs neither, but belongs to the class's topicپێویستی بە هیچیان نییە، بەڵام سەر بە بابەتی

When in doubt, write an instance method. A static method that has nothing to do with the class's topic is better as an ordinary function in a module.

ئەگەر دڵنیا نیت، بنووسە. ستاتیک کە هیچ پەیوەندییەکی بە بابەتی نییە باشترە ببێتە ئاسایی لە مۆدیوڵێکدا.

Worked program: a small schoolبەرنامەیەکی چارەسەرکراو: قوتابخانەیەکی بچووک

Now the pieces work together. A school has students, teachers and courses. Each class lives in a module of its own; people.py holds Student and Teacher:

ئێستا پارچەکان پێکەوە کار دەکەن. قوتابخانەیەک قوتابی، مامۆستا و کۆرسی هەیە. هەر لە مۆدیوڵێکی تایبەتدایە؛ people.py Student و Teacher هەڵدەگرێت:

people.py: Student and Teacher⁦people.py: Student⁩ و Teacherpeople.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, student_id, name):
        self.student_id = student_id
        self.name = name
        self.marks = {}                    # course title -> mark

    def __repr__(self):
        return f"Student({self.student_id}, {self.name!r})"

    def __eq__(self, other):
        if not isinstance(other, Student):
            return NotImplemented
        return self.student_id == other.student_id

    def __hash__(self):
        return hash(self.student_id)


class Teacher:
    def __init__(self, name, subject):
        self.name = name
        self.subject = subject

    def __str__(self):
        return f"{self.name} ({self.subject})"

Student keeps a dictionary of marks (course title → mark), shows itself exactly with __repr__, and is equal to another student with the same number; its __hash__ matches __eq__. Teacher needs only a friendly __str__. The class diagram shows how the three classes fit together.

Student نمرە هەڵدەگرێت (ناوی کۆرس ← نمرە)، بە __repr__ خۆی بە وردی پیشان دەدات و لەگەڵ قوتابییەکی تر کە هەمان ژمارەی هەبێت یەکسانە؛ __hash__ـەکەی لەگەڵ __eq__ دەگونجێت. Teacher تەنیا پێویستی بە __str__ـێکی ڕوون هەیە. پیشان دەدات سێ چۆن پێکەوە دەگونجێن.

A UML class diagram with three boxes. Teacher: name, subject; __str__(). Course: seats = 3 (underlined, a class attribute), title, teacher, students; enrol(student), give_mark(student, mark), is_valid_mark(mark), __len__(), __str__(). Student: student_id, name, marks; __repr__(), __eq__(other), __hash__(). A line marked 1, teaches, joins Course and Teacher; a line marked 0..3, enrolled, joins Course and Student.هێڵکارییەکی کلاسی UML بە سێ چوارگۆشە. Teacher: name، subject؛ __str__(). Course: seats = 3 (هێڵبەژێرداهاتوو، خەسڵەتێکی کلاس)، title، teacher، students؛ enrol(student)، give_mark(student, mark)، is_valid_mark(mark)، __len__()، __str__(). Student: student_id، name، marks؛ __repr__()، __eq__(other)، __hash__(). هێڵێک بە 1 و دەیڵێتەوە Course و Teacher بەیەکەوە دەبەستێت؛ هێڵێک بە 0..3 و تۆمارکراو Course و Student بەیەکەوە دەبەستێت.
The school program as a class diagram: one teacher per course, up to three students. قوتابخانەکە وەک : یەک مامۆستا بۆ هەر کۆرسێک، تا سێ قوتابی.CêIT

Worked program: a small schoolبەرنامەیەکی چارەسەرکراو: قوتابخانەیەکی بچووک

The Course classکلاسی Course

course.py holds Course. A course has a teacher object and a list of student objects: objects inside an object.

course.py Course هەڵدەگرێت. کۆرسێک مامۆستا و قوتابی هەیە: لە ناو .

course.py: Course⁦course.py⁩: کلاسی Coursecourse.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Course:
    seats = 3                              # class attribute: the room's size

    def __init__(self, title, teacher):
        self.title = title
        self.teacher = teacher             # an object inside an object
        self.students = []

    def enrol(self, student):
        if student in self.students:       # uses Student.__eq__
            return f"{student.name} is already in {self.title}"
        if len(self.students) >= Course.seats:
            return f"{self.title} is full"
        self.students.append(student)
        return f"{student.name} joined {self.title}"

    def give_mark(self, student, mark):
        if not Course.is_valid_mark(mark):
            raise ValueError(f"a mark must be 0-100, not {mark}")
        student.marks[self.title] = mark   # the student object keeps it

    @staticmethod
    def is_valid_mark(mark):
        return 0 <= mark <= 100

    def __len__(self):
        return len(self.students)

    def __str__(self):
        lines = [f"{self.title}, taught by {self.teacher}, {len(self)} students"]
        for s in self.students:
            lines.append(f"  {s.name:<8}{s.marks.get(self.title, '-')}")
        return "\n".join(lines)
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 10

in compares with Student.__eq__, so a second object with the same number counts as the same student.

in بە Student.__eq__ بەراورد دەکات، بۆیە دووەم بە هەمان ژمارە وەک هەمان قوتابی دەژمێردرێت.

Lineدێڕی 20

The mark is stored in the student object, so each student carries all their marks.

نمرەکە لە ناو قوتابییەکەدا هەڵدەگیرێت، بۆیە هەر قوتابییەک هەموو نمرەکانی خۆی هەڵدەگرێت.

enrol() returns a message in each case instead of printing it, so the caller decides what to do with it. give_mark() checks the mark with a static method before it stores anything.

enrol() لە هەموو حاڵەتێکدا پەیامێک دەگەڕێنێتەوە لەبری ئەوەی چاپی بکات، بۆیە بڕیار دەدات چی لێ بکات. give_mark() پێش هەڵگرتنی هەر شتێک نمرەکە بە ستاتیک دەپشکنێت.

A flowchart of enrol(student). First question: student in self.students? Yes leads to the message "... is already in". No leads to the second question: len(...) >= Course.seats? Yes leads to "... is full". No leads to append(student) and "... joined". Bottom: in uses Student.__eq__, so a second Aram object is recognised.هێڵکارییەکی ڕەوتی enrol(student). پرسیاری یەکەم: student in self.students؟ بەڵێ دەچێت بۆ پەیامی "... is already in". نەخێر دەچێت بۆ پرسیاری دووەم: len(...) >= Course.seats؟ بەڵێ دەچێت بۆ "... is full". نەخێر دەچێت بۆ append(student) و "... joined". خوارەوە: in بۆ بەراوردکردن Student.__eq__ بەکار دەهێنێت، بۆیە ئۆبجێکتێکی دووەمی ئارام دەناسرێتەوە.
The two checks of enrol() before a student joins.دوو پشکنینەکەی enrol() پێش ئەوەی قوتابییەک بێتە ناو کۆرسەکە.CêIT

Worked program: a small schoolبەرنامەیەکی چارەسەرکراو: قوتابخانەیەکی بچووک

Running the schoolکارپێکردنی قوتابخانەکە

main.py imports the classes (ways to import) and uses them:

main.py هاوردە دەکات (شێوەکانی import) و بەکاریان دەهێنێت:

main.py: one course, five enrolments⁦main.py⁩: یەک کۆرس، پێنج تۆمارکردنmain.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
from people import Student, Teacher
from course import Course

hana = Teacher("Hana", "Computer Science")
python = Course("Python 1", hana)

aram = Student(1021, "Aram")
shilan = Student(1034, "Shilan")
karwan = Student(1040, "Karwan")
dilan = Student(1052, "Dilan")

for s in [aram, shilan, Student(1021, "Aram"), karwan, dilan]:
    print(python.enrol(s))

python.give_mark(aram, 88)
python.give_mark(shilan, 94)
print(python)
print("Aram's marks:", aram.marks)
print(python.students)

The third enrolment is a new Student(1021, "Aram") object, but == says it is Aram, so he is not added twice. Dilan arrives when the 3 seats are taken. print(python) calls Course.__str__, which shows the teacher through Teacher.__str__; the last line shows the students through Student.__repr__.

تۆمارکردنی سێیەم نوێی Student(1021, "Aram")ـە، بەڵام == دەڵێت ئەوە ئارامە، بۆیە دوو جار زیاد ناکرێت. دیلان کاتێک دێت کە 3 کورسییەکە گیراون. print(python) Course.__str__ بانگ دەکات کە مامۆستای کۆرسەکە لە ڕێگەی Teacher.__str__ـەوە پیشان دەدات؛ دوا دێڕ قوتابییەکان لە ڕێگەی Student.__repr__ـەوە پیشان دەدات.

The objects after the run. The Course object python has title "Python 1", a teacher reference pointing to hana: Teacher (name "Hana", subject "Computer Science") and a students list with three references to aram, shilan and karwan. Aram's marks are {'Python 1': 88}, Shilan's {'Python 1': 94} and Karwan's {}. Dilan was not enrolled because the course was full.ئۆبجێکتەکان دوای کارکردن. ئۆبجێکتی Courseـی python title "Python 1"ـی هەیە، ئاماژەیەکی teacher کە ئاماژە بۆ hana: Teacher دەکات (name "Hana"، subject "Computer Science") و لیستی students بە سێ ئاماژە بۆ aram، shilan و karwan. نمرەکانی ئارام {'Python 1': 88}ـن، هی شیلان {'Python 1': 94} و هی کاروان {}. دیلان تۆمار نەکرا چونکە کۆرسەکە پڕ بوو.
After the run: one course object refers to a teacher object and three student objects.دوای کارکردن: یەک کۆرس ئاماژە بۆ مامۆستا و سێ قوتابی دەکات.CêIT

Common mistakesهەڵە باوەکان

Each mistake below was run with Python 3.13; the table shows what happened and the fix.

هەریەکە لەم هەڵانەی خوارەوە بە ⁦3.13⁩ تاقی کرایەوە؛ خشتەکە پیشان دەدات چی ڕوو دا و چارەسەرەکە چییە.

Seven mistakes with attributes and methods (checked with Python 3.13).حەوت هەڵە لەگەڵ و (بە ⁦3.13⁩ پشکنراون).
MistakeهەڵەWhat happensچی ڕوو دەداتFixچارەسەر
self.count += 1 for a class counterself.count += 1 بۆ Each object gets count 1; Student.count stays 0هەر count 1 وەردەگرێت؛ Student.count لە 0 دەمێنێتەوەStudent.count += 1
marks = [] in the class bodymarks = [] لە لاشەی All students share one listهەموو قوتابییەکان یەک لیست بەش دەکەنself.marks = [] in __init__
__str__ that prints instead of returning__str__ـێک کە چاپ دەکات لەبری گەڕاندنەوەTypeError: __str__ returned non-string (type NoneType)return the textدەقەکە return بکە
__eq__ without an isinstance() check__eq__ بێ پشکنینی isinstance()AttributeError: 'str' object has no attribute 'student_id'Return NotImplemented for other typesبۆ جۆرەکانی تر NotImplemented بگەڕێنەوە
__eq__ without __hash__, then a set__eq__ بێ __hash__، پاشان TypeError: unhashable type: 'Student'Add a matching __hash____hash__ـێکی گونجاو زیاد بکە
A helper with no self and no decorator, called through an objectیارمەتیدەرێک بێ self و بێ ، کە لە ڕێگەی بانگ دەکرێتTypeError: Student.is_valid_mark() takes 1 positional argument but 2 were givenAdd @staticmethod@staticmethod زیاد بکە
Changing a class attribute through an objectگۆڕینی لە ڕێگەی Only that object changes (shadowing)تەنیا ئەو دەگۆڕێت ()Assign through the classلە ڕێگەی دابنێ

Common mistakesهەڵە باوەکان

One more trap: a list as a default valueتەڵەیەکی تر: لیستێک وەک نرخی بنەڕەتی

The shared-list trap also hides in a default value. A default is made only once, when Python reads the def (defaults are made once), so every object made without marks shares that one list:

تەڵەی لیستی هاوبەش لە خۆی دەشارێتەوە. تەنیا یەک جار دروست دەکرێت، کاتێک defـەکە دەخوێنێتەوە (نرخی بنەڕەتی یەک جار دروست دەکرێت)، بۆیە هەموو ئەو بێ نمرە دروست دەکرێن ئەو یەک لیستە بەش دەکەن:

marks=[] as a default: wrongmarks=[] وەک نرخی بنەڕەتی: هەڵەmistake_default_list.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name, marks=[]):  # the default list is made only once
        self.name = name
        self.marks = marks

aram = Student("Aram")
hana = Student("Hana")
aram.marks.append(90)
print(hana.marks)

Hana got Aram's mark. Use None as the default and make a new list inside __init__; copying a list you were given with list(marks) also keeps the caller's list safe:

هانا نمرەکەی ئارامی وەرگرت. None وەک بەکار بهێنە و لە ناو __init__ـدا لیستێکی نوێ دروست بکە؛ کۆپیکردنی ئەو لیستەی پێت دراوە بە list(marks) لیستی دەپارێزێت:

marks=None, then a new list: rightmarks=None و پاشان لیستێکی نوێ: ڕاستfixed_default_list.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name, marks=None):
        self.name = name
        if marks is None:
            marks = []                   # a new list on every call
        self.marks = list(marks)         # and a copy of a list we were given

aram = Student("Aram")
hana = Student("Hana")
aram.marks.append(90)
print(aram.marks, hana.marks)

Exercisesڕاهێنانەکان

ExerciseڕاهێنانClass or instance attribute?خەسڵەتی کلاس یان خەسڵەتی نموونە؟

A Course has these attributes: the school's name, the number of seats in every room of the school, the course title, the teacher and the list of students. Which should be class attributes and which instance attributes? Write the class.

Course ئەم هەیە: ناوی قوتابخانەکە، ژمارەی کورسی لە هەموو پۆلێکی قوتابخانەکەدا، ناوی کۆرسەکە، مامۆستای کۆرسەکە و لیستی قوتابییەکان. کامیان دەبێت بن و کامیان ؟ بنووسە.

Show answerوەڵامەکە پیشان بدە

The school name and the seats are the same for every course: class attributes. The title, the teacher and the students differ for each course: instance attributes, and the list must be made in __init__.

ناوی قوتابخانە و کورسییەکان بۆ هەموو کۆرسێک وەک یەکن: . ناوی کۆرس، مامۆستا و قوتابییەکان بۆ هەر کۆرسێک جیاوازن: و دەبێت لیستەکە لە __init__ـدا دروست بکرێت.

One possible answerوەڵامێکی گونجاوattr_kinds.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Course:
    school = "Azadi School"              # the same for every course
    seats = 30                           # every room in this school has 30 seats

    def __init__(self, title, teacher):
        self.title = title               # different for each course
        self.teacher = teacher
        self.students = []               # a new list for each course

maths = Course("Maths", "Hana")
python = Course("Python 1", "Soran")
print(maths.school, maths.seats, maths.title, len(maths.students))
print(python.school, python.seats, python.title, python.teacher)
ExerciseڕاهێنانPredict the outputدەرخستەکە پێشبینی بکە

Without running it, write down what this program prints.

بەبێ کارپێکردنی، بنووسە ئەم چی چاپ دەکات.

predict_classroom.pyPYTHON
class Classroom:
    capacity = 30

    def __init__(self, name):
        self.name = name

a = Classroom("10A")
b = Classroom("10B")
a.capacity = 25
Classroom.capacity = 32
print(a.capacity, b.capacity, Classroom.capacity)
Show answerوەڵامەکە پیشان بدە
The outputدەرخستەکەpredict_classroom.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Classroom:
    capacity = 30

    def __init__(self, name):
        self.name = name

a = Classroom("10A")
b = Classroom("10B")
a.capacity = 25
Classroom.capacity = 32
print(a.capacity, b.capacity, Classroom.capacity)

a.capacity = 25 made an instance attribute in a, which hides the class attribute: 25. Then the class attribute became 32, which b reads: 32, and Classroom.capacity is 32.

a.capacity = 25 لە aـدا دروست کرد کە دەشارێتەوە: ⁦25⁩. پاشان بوو بە ⁦32⁩ کە b دەیخوێنێتەوە: ⁦32⁩ و Classroom.capacity ⁦32⁩ـە.

Exerciseڕاهێنان__str__ and __repr__ for Teacher__str__ و __repr__ بۆ Teacher

Give the class Teacher(name, subject) a __str__ that returns "Hana teaches Maths" and a __repr__ that returns "Teacher('Hana', 'Maths')". Print one teacher, its repr() and a list of two teachers.

__str__ بدە بە Teacher(name, subject) کە "Hana teaches Maths" بگەڕێنێتەوە و __repr__ـێک کە "Teacher('Hana', 'Maths')" بگەڕێنێتەوە. مامۆستایەک، repr()ـەکەی و دوو مامۆستا چاپ بکە.

Hintئاماژە

Use !r inside the f-string of __repr__ so the names keep their quotes.

لە ناو __repr__ـدا !r بەکار بهێنە بۆ ئەوەی ناوەکان کەوانەکانیان بمێنێت.

Show answerوەڵامەکە پیشان بدە
One possible answerوەڵامێکی گونجاوteacher_str_repr.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Teacher:
    def __init__(self, name, subject):
        self.name = name
        self.subject = subject

    def __str__(self):
        return f"{self.name} teaches {self.subject}"

    def __repr__(self):
        return f"Teacher({self.name!r}, {self.subject!r})"

hana = Teacher("Hana", "Maths")
print(hana)
print(repr(hana))
print([hana, Teacher("Soran", "Physics")])
ExerciseڕاهێنانA class method from a dictionaryمیتۆدێکی کلاس لە فەرهەنگێکەوە

Add a class method Teacher.from_dict(data) that makes a teacher from a dictionary such as {"name": "Soran", "subject": "Physics"}.

Teacher.from_dict(data) زیاد بکە کە مامۆستایەک لە وەک {"name": "Soran", "subject": "Physics"} دروست بکات.

Show answerوەڵامەکە پیشان بدە
One possible answerوەڵامێکی گونجاوteacher_from_dict.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Teacher:
    def __init__(self, name, subject):
        self.name = name
        self.subject = subject

    @classmethod
    def from_dict(cls, data):
        return cls(data["name"], data["subject"])

    def __repr__(self):
        return f"Teacher({self.name!r}, {self.subject!r})"

row = {"name": "Soran", "subject": "Physics"}
soran = Teacher.from_dict(row)
print(soran)
ExerciseڕاهێنانThe top studentباشترین قوتابی

Add a method top_student() to Course that returns the student with the highest mark in this course, or None when nobody has a mark yet.

top_student() بۆ Course زیاد بکە کە ئەو قوتابییە بگەڕێنێتەوە کە بەرزترین نمرەی لەم کۆرسەدا هەیە، یان None کاتێک هێشتا کەس نمرەی نییە.

Show answerوەڵامەکە پیشان بدە
One possible answer (a shorter school model)وەڵامێکی گونجاو (مۆدێلێکی کورتتری قوتابخانە)top_student.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Student:
    def __init__(self, name):
        self.name = name
        self.marks = {}                    # course title -> mark

class Course:
    def __init__(self, title):
        self.title = title
        self.students = []

    def enrol(self, student):
        self.students.append(student)

    def give_mark(self, student, mark):
        student.marks[self.title] = mark

    def top_student(self):
        best = None
        for s in self.students:
            if self.title not in s.marks:
                continue                   # no mark yet in this course
            if best is None or s.marks[self.title] > best.marks[self.title]:
                best = s
        return best

python = Course("Python 1")
print(python.top_student())                # no students yet
for name in ["Aram", "Shilan", "Karwan"]:
    python.enrol(Student(name))
python.give_mark(python.students[0], 88)
python.give_mark(python.students[1], 94)
print(python.top_student().name)

best starts as None. Students without a mark in this course are skipped with continue, so Karwan does not break the comparison.

best بە None دەست پێ دەکات. ئەو قوتابییانەی لەم کۆرسەدا نمرەیان نییە بە continue تێدەپەڕێنرێن، بۆیە کاروان بەراوردەکە تێک نادات.

ExerciseڕاهێنانA static method for titlesمیتۆدێکی ستاتیک بۆ ناوی کۆرس

Add a static method Course.is_valid_title(title) that is True when the title, without spaces at the ends, has 1 to 40 characters. Use it in __init__ to refuse a bad title with a ValueError.

ستاتیکی Course.is_valid_title(title) زیاد بکە کە True بێت کاتێک ناوەکە، بەبێ بۆشایی لە دوو سەرەکەیدا، 1 تا ⁦40⁩ بێت. لە __init__ـدا بەکاری بهێنە بۆ ڕەتکردنەوەی ناوێکی هەڵە بە ValueError.

Show answerوەڵامەکە پیشان بدە
One possible answerوەڵامێکی گونجاوvalid_title.pyPYTHONType in a terminalلە تێرمیناڵدا بینووسە
class Course:
    def __init__(self, title):
        if not Course.is_valid_title(title):
            raise ValueError(f"bad course title: {title!r}")
        self.title = title.strip()

    @staticmethod
    def is_valid_title(title):
        return 0 < len(title.strip()) <= 40

print(Course.is_valid_title("Python 1"), Course.is_valid_title("   "))
c = Course("  Python 1 ")
print(repr(c.title))
Course("")

Summaryپوختە

  • Instance attributes (self.x in __init__) belong to one object; class attributes (in the class body) are shared. Reading obj.x looks in the object, then in the class.
  • Change class attributes through the class (Student.count += 1); assigning through an object makes a new instance attribute (shadowing). Make lists in __init__, never in the class body or as a default.
  • Instance methods (self) change an object or return an answer; check new values before changing the state.
  • Special methods are called by Python: __str__ (people), __repr__ (programmers), __eq__ (==, in), __hash__ (sets, keys), __len__ (len()). All __str__/__repr__ must return strings.
  • A decorator line changes a method: @classmethod passes the class as cls (alternative constructors); @staticmethod passes nothing extra (helpers).
  • Classes work together: a course has a teacher object and a list of student objects.
  • (self.x لە __init__ـدا) هی یەک ؛ (لە لاشەی ) هاوبەشن. خوێندنەوەی obj.x لە دەگەڕێت، پاشان لە .
  • لە ڕێگەی بگۆڕە (Student.count += 1)؛ دانانی لە ڕێگەی نوێ دروست دەکات (). لیستەکان لە __init__ـدا دروست بکە، هەرگیز لە لاشەی یان وەک .
  • (self) دەگۆڕن یان وەڵامێک دەگەڕێننەوە؛ پێش گۆڕینی دۆخ نوێ بپشکنە.
  • بانگیان دەکات: __str__ (خەڵک)، __repr__ ()، __eq__ (==، in)، __hash__ (، )، __len__ (len()). __str__ و __repr__ دەبێت بگەڕێننەوە.
  • دێڕێکی دەگۆڕێت: @classmethod وەک cls دەنێرێت ( جێگرەوە)؛ @staticmethod هیچی زیادە نانێرێت (یارمەتیدەرەکان).
  • پێکەوە کار دەکەن: کۆرسێک مامۆستا و قوتابی هەیە.

Next: Encapsulation and properties protects attributes, and Inheritance and polymorphism builds classes from classes. Compare with Java in Python and Java.

داهاتوو: کەپسوولکردن و تایبەتمەندییەکان دەپارێزێت و بۆماوە و فرەشێوەیی لە دروست دەکات. بەراوردی بکە لەگەڵ لە Python و Java.

Lesson complete!وانەکە تەواو بوو!

Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.

Key termsزاراوە سەرەکییەکان

Next lessonوانەی داهاتوو

Encapsulation and Propertiesکەپسوولکردن و خەسڵەتە بەڕێوەبراوەکان

References and further readingسەرچاوەکان و خوێندنەوەی زیاتر

This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.

Appearanceڕووکار

Modeدۆخ
Styleشێواز
Coloursڕەنگەکان

Saved on this device only.تەنیا لەسەر ئەم ئامێرە پاشەکەوت دەکرێت.