http___programarcadegames.com_index.php_chapter_introduction_to_classes_lang_tr_2.xml
1.1 2.1 3.1 4.1 5.1 6.1 7.1 8.1 9.1 10.1 11.1 12.1 13.1 13.2 13.3 14.1 15.1 16.1 17.1 18.1 18.2 18.3 18.4 19.1 19.2 19.3 20.1 21.1 21.2 21.3 21.4 21.5 21.6 21.7 22.1 23.1 23.2 23.3 24.1 25.1 26.1 27.1 28.1 28.2 28.3 29.1 29.2 30.1 30.2 31.1 31.2 31.3 31.4 31.5 31.6 31.7 32.1 32.2 32.3 32.4 33.1 33.2 33.3 33.4 33.5 34.1 35.1 35.2 35.3 36.1 36.2 36.3 37.1 37.2 37.3 37.4 38.1 38.2 38.3 39.1 39.2 40.1 41.1 41.2 42.1 42.2 42.3 43.1 43.2 43.3 43.4 44.1 44.2 44.3 44.4 45.1 45.2 45.3 46.1 47.1 48.1 48.2 48.3 49.1 50.1 50.2 50.3 50.4 50.5 51.1 52.1 53.1 54.1 55.1 55.2 56.1 57.1 57.2 57.3 57.4 57.5 57.6 58.1 59.1 59.2 59.3 60.1 61.1 61.2 62.1 63.1 63.2 63.3 64.1 65.1 66.1 67.1 68.1 69.1 70.1 70.2 70.3 71.1 71.2 72.1 73.1 73.2 74.1 74.2 75.1 76.1 76.2 76.3 76.4 77.1 77.2 77.3 78.1 78.2 79.1 79.2 79.3 79.4 79.5 79.6 80.1 81.1 82.1 82.2 82.3 82.4 82.5 83.1 84.1 84.2 85.1 85.2 86.1 87.1 87.2 87.3 87.4 87.5 88.1 88.2 88.3 89.1 90.1 90.2 90.3 91.1 92.1 92.2 92.3 92.4 93.1 94.1 94.2 94.3 94.4 94.5 95.1 96.1 96.2 96.3 96.4 97.1 97.2 98.1 98.2 98.3 98.4 99.1 100.1 101.1 102.1 103.1 104.1 104.2 104.3 104.4 104.5 104.6 105.1 106.1 106.2 106.3 106.4 106.5 106.6 106.7 107.1 108.1 109.1 110.1 111.1 112.1 113.1 114.1 115.1 116.1 116.2 117.1 118.1 118.2 119.1 120.1 120.2 120.3 121.1 122.1 123.1 124.1 124.2 124.3 125.1 125.2 125.3 126.1 126.2 126.3 127.1 127.2 128.1 128.2 128.3 128.4 128.5 129.1 129.2 129.3 129.4 130.1 131.1 131.2 131.3 131.4 132.1 133.1 134.1 134.2 135.1 135.2 136.1 137.1 137.2 138.1 139.1 140.1 141.1 142.1 142.2 143.1 143.2 143.3 143.4 143.5 143.6 143.7 143.8 144.1 144.2 144.3 144.4 144.5 145.1 146.1 147.1 148.1 149.1 150.1 151.1 152.1 152.2 152.3 152.4 153.1 153.2 154.1 154.2 154.3 154.4 155.1 155.2 155.3 156.1 156.2 156.3 156.4 156.5 157.1 157.2 158.1 158.2 158.3 158.4 159.1 159.2 159.3 159.4 159.5 160.1 160.2 161.1 162.1 162.2 162.3 163.1 163.2 163.3 163.4 163.5 163.6 163.7 163.8 163.9 163.10 164.1 165.1 166.1 167.1 168.1 169.1 171.1 172.1 172.2 173.1 174.1 175.1 176.1 177.1 178.1 179.1
Program Arcade GamesWith Python And Pygame English Dutch - Nederlands French Hungarian - Magyar Portuguese - Português Russian - Pycckий Spanish - Español Turkish - Türkçe Chapter 13: Introduction to Classes Package up related variables, and give them life! 13.1 Why Learn About Classes? Each character in an adventure game needs data: a name, location, strength, are they raising their arm, what direction they are headed, etc. Plus those characters do things. They run, jump, hit, and talk. Our Python code to store this data might look like: name = "Link" sex = "Male" max_hit_points = 50 current_hit_points = 50 In order to do anything with this character, we'll need to pass that data to a function: def display_character(name, sex, max_hit_points, current_hit_points): print(name, sex, max_hit_points, current_hit_points) Now imagine creating a program that has a set of variables like that for each character, monster, and item in our game. Then we need to create functions that work with those items. We've now waded into a quagmire of data. All of a sudden this doesn't sound like fun at all. But wait, it gets worse! As our game expands, we may need to add new fields to describe our character. In this case we've added max_speed: name = "Link" sex = "Male" max_hit_points = 50 current_hit_points = 50 max_speed = 10 def display_character(name, sex, max_hit_points, current_hit_points, max_speed): print(name, sex, max_hit_points, current_hit_points) In example above, there is only one function. But in a large video game, we might have hundreds of functions that deal with the main character. Adding a new field to help describe what character has and can do would require us to go through each one of those functions and add it to the parameter list. That would be a lot of work. And perhaps we need to add max_speed to different types of characters like monsters. There needs to be a better way. Somehow our program needs to package up those data fields so they can be managed easily. 13.2 Defining and Creating Simple Classes A better way to manage multiple data attributes is to define a structure that has all of the information. Then we can give that “grouping” of information a name, like Character or Address. This can be easily done in Python and any other modern language by using a class. For example, we can define a class representing a character in a game: class Character(): def __init__(self): self.name = "Link" self.sex = "Male" self.max_hit_points = 50 self.current_hit_points = 50 self.max_speed = 10 self.armor_amount = 8 Here's another example, we define a class to hold all the fields for an address: class Address(): def __init__(self): self.name = "" self.line1 = "" self.line2 = "" self.city = "" self.state = "" self.zip = "" In the code above, Address is the class name. The variables in the class, such as name and city, are called attributes or fields. (Note the similarities and differences between declaring a class and declaring a function.) Unlike functions and variables, class names should begin with an upper case letter. While it is possible to begin a class with a lower case letter, it is not considered good practice. The def __init__(self): in a special function called a constructor that is run automatically when the class is created. We'll discuss the constructor more in a bit. The self. is kind of like the pronoun my. When inside the class Address we are talking about my name, my city, etc. We don't want to use self. outside of the class definition for Address, to refer to an Address field. Why? Because just like the pronoun “my,” it means someone totally different when said by a different person! To better visualize classes and how they relate, programmers often make diagrams. A diagram for the Address class would look like Figure 13.1. See how the class name is on top with the name of each attribute listed below. To the right of each attribute is the data type, such as string or integer. The class code defines a class but it does not actually create an instance of one. The code told the computer what fields an address has and what the initial default values will be. We don't actually have an address yet though. We can define a class without creating one just like we can define a function without calling it. To create a class and set the fields, look at the example below: # Create an address home_address = Address() # Set the fields in the address home_address.name = "John Smith" home_address.line1 = "701 N. C Street" home_address.line2 = "Carver Science Building" home_address.city = "Indianola" home_address.state = "IA" home_address.zip = "50125" An instance of the address class is created in line 2. Note how the class Address name is used, followed by parentheses. The variable name can be anything that follows normal naming rules. To set the fields in the class, a program must use the dot operator. This operator is the period that is between the home_address and the field name. See how lines 5-10 use the dot operator to set each field value. A very common mistake when working with classes is to forget to specify which instance of the class you want to work with. If only one address is created, it is natural to assume the computer will know to use that address you are talking about. This is not the case however. See the example below: class Address(): def __init__(self): self.name = "" self.line1 = "" self.line2 = "" self.city = "" self.state = "" self.zip = "" # Create an address my_address = Address() # Alert! This does not set the address's name! name = "Dr. Craven" # This doesn't set the name for the address either Address.name = "Dr. Craven" # This does work: my_address.name = "Dr. Craven" A second address can be created and fields from both instances may be used. See the example below: class Address(): def __init__(self): self.name = "" self.line1 = "" self.line2 = "" self.city = "" self.state = "" self.zip = "" # Create an address home_address = Address() # Set the fields in the address home_address.name = "John Smith" home_address.line1 = "701 N. C Street" home_address.line2 = "Carver Science Building" home_address.city = "Indianola" home_address.state = "IA" home_address.zip = "50125" # Create another address vacation_home_address = Address() # Set the fields in the address vacation_home_address.name = "John Smith" vacation_home_address.line1 = "1122 Main Street" vacation_home_address.line2 = "" vacation_home_address.city = "Panama City Beach" vacation_home_address.state = "FL" vacation_home_address.zip = "32407" print("The client's main home is in " + home_address.city) print("His vacation home is in " + vacation_home_address.city) Line 11 creates the first instance of Address; line 22 creates the second instance. The variable home_address points to the first instance and vacation_home_address points to the second. Lines 25-30 set the fields in this new class instance. Line 32 prints the city for the home address, because home_address appears before the dot operator. Line 33 prints the vacation address because vacation_home_address appears before the dot operator. In the example Address is called the class because it defines a new classification for a data object. The variables home_address and vacation_home_address refer to objects because they refer to actual instances of the class Address. A simple definition of an object is that it is an instance of a class. Like “Bob” and “Nancy” are instances of a Human class. By using www.pythontutor.com we can visualize the execution of the code (see below). There are three variables in play. One points to the class definition of Address. The other two variables point to the different address objects and their data. Putting lots of data fields into a class makes it easy to pass data in and out of a function. In the code below, the function takes in an address as a parameter and prints it out on the screen. It is not necessary to pass parameters for each field of the address. # Print an address to the screen def print_address(address): print(address.name) # If there is a line1 in the address, print it if len(address.line1) > 0: print(address.line1) # If there is a line2 in the address, print it if len(address.line2) > 0: print( address.line2 ) print(address.city + ", " + address.state + " " + address.zip) print_address(home_address) print() print_address(vacation_home_address) 13.3 Adding Methods to Classes In addition to attributes, classes may have methods. A method is a function that exists inside of a class. Expanding the earlier example of a Dog class from the review problem 1 above, the code below adds a method for a dog barking. class Dog(): def __init__(self): self.age = 0 self.name = "" self.weight = 0 def bark(self): print("Woof") The method definition is contained in lines 6-7 above. Method definitions in a class look almost exactly like function definitions. The big difference is the addition of a parameter self on line 6. The first parameter of any method in a class must be self. This parameter is required even if the function does not use it. Here are the important items to keep in mind when creating methods for classes: - Attributes should be listed first, methods after. - The first parameter of any method must be self. - Method definitions are indented exactly one tab stop. Methods may be called in a manner similar to referencing attributes from an object. See the example code below. my_dog = Dog() my_dog.name = "Spot" my_dog.weight = 20 my_dog.age = 3 my_dog.bark() Line 1 creates the dog. Lines 3-5 set the attributes of the object. Line 7 calls the bark function. Note that even through the bark function has one parameter, self, the call does not pass in anything. This is because the first parameter is assumed to be a reference to the dog object itself. Behind the scenes, Python makes a call that looks like: # Example, not actually legal Dog.bark(my_dog) If the bark function needs to make reference to any of the attributes, then it does so using the self reference variable. For example, we can change the Dog class so that when the dog barks, it also prints out the dog's name. In the code below, the name attribute is accessed using a dot operator and the self reference. def bark(self): print("Woof says", self.name) Attributes are adjectives, and methods are verbs. The drawing for the class would look like Figure 13.3. 13.3.1 Example: Ball Class This example code could be used in Python/Pygame to draw a ball. Having all the parameters contained in a class makes data management easier. The diagram for the Ball class is shown in Figure 13.4. class Ball(): def __init__(self): # --- Class Attributes --- # Ball position self.x = 0 self.y = 0 # Ball's vector self.change_x = 0 self.change_y = 0 # Ball size self.size = 10 # Ball color self.color = [255,255,255] # --- Class Methods --- def move(self): self.x += self.change_x self.y += self.change_y def draw(self, screen): pygame.draw.circle(screen, self.color, [self.x, self.y], self.size ) Below is the code that would go ahead of the main program loop to create a ball and set its attributes: theBall = Ball() theBall.x = 100 theBall.y = 100 theBall.change_x = 2 theBall.change_y = 1 theBall.color = [255,0,0] This code would go inside the main loop to move and draw the ball: theBall.move() theBall.draw(screen) 13.4 References Here's where we separate the true programmers from the want-to-be's. Understanding class references. Take a look at the following code: class Person: def __init__(self): self.name = "" self.money = 0 bob = Person() bob.name = "Bob" bob.money = 100 nancy = Person() nancy.name = "Nancy" print(bob.name, "has", bob.money, "dollars.") print(nancy.name, "has", nancy.money, "dollars.") The code above creates two instances of the Person() class, and using www.pythontutor.com we can visualize the two classes in Figure 13.5. The code above has nothing new. But the code below does: class Person: def __init__(self): self.name = "" self.money = 0 bob = Person() bob.name = "Bob" bob.money = 100 nancy = bob nancy.name = "Nancy" print(bob.name, "has", bob.money, "dollars.") print(nancy.name, "has", nancy.money, "dollars.") See the difference on line 9? A common misconception when working with objects is to assume that the variable bob is the Person object. This is not the case. The variable bob is a reference to the Person object. That is, it stores the memory address of where the object is, and not the object itself. If bob actually was the object, then line 9 could create a copy of the object and there would be two objects in existence. The output of the program would show both Bob and Nancy having 100 dollars. But when run, the program outputs the following instead: Nancy has 100 dollars. Nancy has 100 dollars. What bob stores is a reference to the object. Besides reference, one may call this address, pointer, or handle. A reference is an address in computer memory for where the object is stored. This address is a hexidecimal number which, if printed out, might look something like 0x1e504. When line 9 is run, the address is copied rather than the entire object the address points to. See Figure 13.6. We can also run this in www.pythontutor.com to see how both of the variables are pointing to the same object. 13.4.1 Functions and References Look at the code example below. Line 1 creates a function that takes in a number as a parameter. The variable money is a variable that contains a copy of the data that was passed in. Adding 100 to that number does not change the number that was stored in bob.money on line 10. Thus, the print statement on line 13 prints out 100, and not 200. def giveMoney1(money): money += 100 class Person: def __init__(self): self.name = "" self.money = 0 bob = Person() bob.name = "Bob" bob.money = 100 giveMoney1(bob.money) print(bob.money) Running on PythonTutor we see that there are two instances of the money variable. One is a copy and local to the giveMoney1 function. Look at the additional code below. This code does cause bob.money to increase and the print statement to print 200. def giveMoney2(person): person.money += 100 giveMoney2(bob) print(bob.money) Why is this? Because person contains a copy of the memory address of the object, not the actual object itself. One can think of it as a bank account number. The function has a copy of the bank account number, not a copy of the whole bank account. So using the copy of the bank account number to deposit 100 dollars causes Bob's bank account balance to go up. Arrays work the same way. A function that takes in an array (list) as a parameter and modifies values in that array will be modifying the same array that the calling code created. The address of the array is copied, not the entire array. 13.4.2 Review Questions - Create a class called Cat. Give it attributes for name, color, and weight. Give it a method called meow. - Create an instance of the cat class, set the attributes, and call the meow method. - Create a class called Monster. Give it an attribute for name and an integer attribute for health. Create a method called decreaseHealth that takes in a parameter amount and decreases the health by that much. Inside that method, print that the animal died if health goes below zero. 13.5 Constructors There's a terrible problem with our class for Dog listed below. When we create a dog, by default the dog has no name. Dogs should have names! We should not allow dogs to be born and then never be given a name. Yet the code below allows this to happen, and that dog will never have a name. class Dog() def __init__(self): self.name = "" my_dog = Dog() Python doesn't want this to happen. That's why Python classes have a special function that is called any time an instance of that class is created. By adding a function called a constructor, a programmer can add code that is automatically run each time an instance of the class is created. See the example constructor code below: class Dog(): def __init__(self): self.name = "" # Constructor # Called when creating an object of this type def __init__(self): print("A new dog is born!") # This creates the dog my_dog = Dog() The constructor starts on line 6. It must be named __init__. There are two underscores before the init, and two underscores after. A common mistake is to only use one. The constructor must take in self as the first parameter just like other methods in a class. When the program is run, it will print: A new dog is born! When a Dog object is created on line 10, the __init__ function is automatically called and the message is printed to the screen. A constructor can be used for initializing and setting data for the object. The example Dog class above still allows the name attribute to be left blank after the creation of the dog object. How do we keep this from happening? Many objects need to have values right when they are created. The constructor function can be used to make this happen. See the code below: class Dog(): # Constructor # Called when creating an object of this type def __init__(self, new_name): self.name = new_name # This creates the dog my_dog = Dog("Spot") # Print the name to verify it was set print(my_dog.name) # This line will give an error because # a name is not passed in. herDog = Dog() On line 6 the constructor function now has an additional parameter named new_name. The value of this parameter is used to set the name attribute in the Dog class on line 7. It is no longer possible to create a Dog class without a name. The code on line 17 tries this. It will cause a Python error and it will not run. A common mistake is to name the parameter of the __init__ function the same as the attribute and assume that the values will automatically synchronize. This does not happen. 13.5.1 Review Questions - Should class names begin with an upper or lower case letter? - Should method names begin with an upper or lower case letter? - Should attribute names begin with an upper or lower case letter? - Which should be listed first in a class, attributes or methods? - What are other names for a reference? - What is another name for instance variable? - What is the name for an instance of a class? - Create a class called Star that will print out “A star is born!” every time it is created. - Create a class called Monster with attributes for health and a name. Add a constructor to the class that sets the health and name of the object with data passed in as parameters. 13.6 Inheritance Another powerful feature of using classes and objects is the ability to make use of inheritance. It is possible to create a class and inherit all of the attributes and methods of a parent class. For example, a program may create a class called Boat which has all the attributes needed to represent a boat in a game: class Boat(): def __init__(self): self.tonnage = 0 self.name = "" self.isDocked = True def dock(self): if self.isDocked: print("You are already docked.") else: self.isDocked = True print("Docking") def undock(self): if not self.isDocked: print("You aren't docked.") else: self.isDocked = False print("Undocking") To test out our code: b = Boat() b.dock() b.undock() b.undock() b.dock() b.dock() The outputs: You are already docked. Undocking You aren't docked. Docking You are already docked. (If you watch the video for this section of the class, you'll note that the "Boat" class in the video doesn't actually run. The code above has been corrected but I haven't fixed the video. Use this as a reminder, no matter how simple the code and how experienced the developer, test your code before you deliver it!) Our program also needs a submarine. Our submarine can do everything a boat can, plus we need a command for submerge. Without inheritance we have two options. - One, add the submerge() command to our boat. This isn't a great idea because we don't want to give the impression that our boats normally submerge. - Two, we could create a copy of the Boat class and call it Submarine. In this class we'd add the submerge() command. This is easy at first, but things become harder if we change the Boat class. A programmer would need to remember that we'd need to change not only the Boat class, but also make the same changes to the Submarine class. Keeping this code syncronized is time consuming and error-prone. Luckily, there is a better way. Our program can create child classes that will inherit all the attributes and methods of the parent class. The child classes may then add fields and methods that correspond to their needs. For example: class Submarine(Boat): def submerge(self): print("Submerge!") Line 1 is the important part. Just by putting Boat in between the parentheses during the class declaration, we have automatically picked up every attribute and method that is in the Boat class. If we update Boat, then the child class Submarine will automatically get these updates. Inheritance is that easy! The next code example is diagrammed out in Figure 13.10. class Person(): def __init__(self): self.name = "" class Employee(Person): def __init__(self): self.job_title = "" class Customer(Person): def __init__(self): self.email = "" johnSmith = Person() johnSmith.name = "John Smith" janeEmployee = Employee() janeEmployee.name = "Jane Employee" janeEmployee.job_title = "Web Developer" bobCustomer = Customer() bobCustomer.name = "Bob Customer" bobCustomer.email = "send_me@spam.com" By placing Person between the parentheses on lines 4 and 7, the programmer has told the computer that Person is a parent class to both Employee and Customer. This allows the program to set the name attribute on lines 14 and 18. Methods are also inherited. The code below will print out “Person created” three times because the employee and customer classes inherit the constructor from the parent class: class Person(): def __init__(self): self.name = "" print("Person created") class Employee(Person): def __init__(self): self.job_title = "" class Customer(Person): def __init__(self): self.email = "" johnSmith = Person() janeEmployee = Employee() bobCustomer = Customer() Methods may be overridden by a child class to provide different functionality. In the code below, the child classes have their own constructors, so the parent's class will not be run: class Person(): def __init__(self): self.name = "" print("Person created") class Employee(Person): def __init__(self): self.job_title = "" print("Employee created") class Customer(Person): def __init__(self): self.email = "" print("Customer created") johnSmith = Person() janeEmployee = Employee() bobCustomer = Customer() If the programmer desires to run both the parent and the child class's method, the child may explicitly call the parent's method as shown in lines 11 and 18: class Person(): def __init__(self): self.name = "" print("Person created") class Employee(Person): def __init__(self): self.job_title = "" super().__init__() print("Employee created") class Customer(Person): def __init__(self): super().__init__() self.email = "" print("Customer created") johnSmith = Person() janeEmployee = Employee() bobCustomer = Customer() 13.6.1 Is-A and Has-A Relationships Classes have two main types of relationships. They are “is a” and “has a” relationships. A parent class should always be a more general, abstract version of the child class. This type of child to parent relationship is called an is a relationship. For example, a parent class Animal could have a child class Dog. The Dog class could have a child class Poodle. Another example, a dolphin is a mammal. It does not work the other way, a mammal is not necessarily a dolphin. So the class Dolphin should never be a parent to a class Mammal. Likewise a class Table should not be a parent to a class Chair because a chair is not a table. The other type of relationship is the has a relationship. These relationships are implemented in code by class attributes. A dog has a name, and so the Dog class has an attribute for name. Likewise a person could have a dog, and that would be implemented by having the Person class have an attribute for Dog. The Person class would not derive from Dog because that would be some kind of insult. Looking at the prior code example we can see: - Employee is a person. - Customer is a person. - Person has a name. - Employee has a job title. - Customer has an e-mail. 13.7 Static Variables vs. Instance Variables The difference between static and instance variables is confusing. Thankfully it isn't necessary to completely understand the difference right now. But if you stick with programming, it will be. Therefore we will briefly introduce it here. There are also some oddities with Python that kept me confused the first several years I've made this book available. So you might see older videos and examples where I get it wrong. An instance variable is the type of class variable we've used so far. Each instance of the class gets its own value. For example, in a room full of people each person will have their own age. Some of the ages may be the same, but we still need to track each age individually. With instance variables, we can't just say “age” with a room full of people. We need to specify whose age we are talking about. Also, if there are no people in the room, then referring to an age when there are no people to have an age makes no sense. With static variables the value is the same for every single instance of the class. Even if there are no instances, there still is a value for a static variable. For example, we might have a count static variable for the number of Human classes in existence. No humans? The value is zero, but it still exists. In the example below, ClassA creates an instance variable. ClassB creates a static variable. # Example of an instance variable class ClassA(): def __init__(self): self.y = 3 # Example of a static variable class ClassB(): x = 7 # Create class instances a = ClassA() b = ClassB() # Two ways to print the static variable. # The second way is the proper way to do it. print(b.x) print(ClassB.x) # One way to print an instance variable. # The second generates an error, because we don't know what instance # to reference. print(a.y) print(ClassA.y) In the example above, lines 16 and 17 print out the static variable. Line 17 is the “proper” way to do so. Unlike before, we can refer to the class name when using static variables, rather than a variable that points to a particular instance. Because we are working with the class name, by looking at line 17 we instantly can tell we are working with a static variable. Line 16 could be an either be instance or static variable, so that confusion makes line 17 the better choice. Line 22 prints out the instance variable, just like we've done in prior examples. Line 23 will generate an error because each instance of y is different (it is an instance variable after all) and we aren't telling the computer what instance of ClassA we are talking about. 13.7.1 Instance Variables Hiding Static Variables This is one “feature” of Python I dislike. It is possible to have a static variable, and an instance variable with the same name. Look at the example below: # Class with a static variable class ClassB(): x = 7 # Create a class instance b = ClassB() # This prints 7 print(b.x) # This also prints 7 print(ClassB.x) # Set x to a new value using the class name ClassB.x = 8 # This also prints 8 print(b.x) # This prints 8 print(ClassB.x) # Set x to a new value using the instance. # Wait! Actually, it doesn't set x to a new value! # It creates a brand new variable, x. This x # is an instance variable. The static variable is # also called x. But they are two different # variables. This is super-confusing and is bad # practice. b.x = 9 # This prints 9 print(b.x) # This prints 8. print(ClassB.x) Allowing instance variables to hide static variable caused confusion for me for many years! 13.8 Review 13.8.1 Multiple Choice Quiz Click here for a multiple-choice quiz. 13.8.2 Short Answer Worksheet Click here for the chapter worksheet. Click here for the chapter lab. You are not logged in. Log in here and track your progress. English version by Paul Vincent Craven Spanish version by Antonio Rodríguez Russian version by Vladimir Slav Turkish version by Güray Yildirim Portuguese version by Armando Marques Sobrinho and Tati Carvalho Dutch version by Frank Waegeman Hungarian version by Nagy Attila French version by Franco Rossi
1.1 2.1 3.1 4.1 5.1 6.1 7.1 8.1 9.1 10.1 11.1 12.1 13.1 14.1 15.1 16.1 17.1 18.1 19.1 20.1 21.1 22.1 24.1 25.1 26.1 27.1 28.1 29.1 30.1 31.1 32.1 33.1 34.1 35.1 36.1 37.1 38.1 39.1 40.1 41.1 42.1 43.1 44.1 45.1 46.1 47.1 48.1 49.1 50.1 51.1 52.1 53.1 54.1 55.1 56.1 57.1 58.1 59.1 60.1 61.1 62.1 63.1 64.1 65.1 66.1 67.1 68.1 69.1 70.1 71.1 72.1 73.1 74.1 75.1 76.1 77.1 78.1 79.1 80.1 82.1 83.1 84.1 85.1 86.1 87.1 88.1 89.1 90.1 91.1 92.1 93.1 94.1 95.1 96.1 97.1 98.1 99.1 100.1 102.1 103.1 104.1 105.1 106.1 107.1 108.1 109.1 110.1 111.1 112.1 113.1 114.1 115.1 116.1 117.1 118.1 119.1 120.1 121.1 122.1 124.1 125.1 126.1 127.1 128.1 129.1 130.1 131.1 132.1 133.1 134.1 135.1 136.1 137.1 138.1 139.1 140.1 141.1 142.1 143.1 144.1 145.1 146.1 147.1
Arcade Türü Oyun Programlamayıve Bilgisayar Bilimleri Öğrenin Chapter 13: Sınıflara Giriş 13.1 Basit Sınıfların Tanımlanması ve Oluşturulması Programcı verinin çok sayıda ilgili parçasını fonksiyona aktarmak isterse, bunu sadece değişkenleri kullanarak yapmak zor olabilir. Daha iyi bir yol ise tüm bilgileri bir araya getiren bir veri yapısı tanımlayıp daha sonra bilgiye Address gibi bir isim vermektir. Bu, Python ve diğer modern dillerde sınıflar (class) kullanılarak kolayca yapılabilir. Bu kod, bir adresi tutan örnek bir sınıftır: class Address(): name="" line1="" line2="" city="" state="" zip="" Yukarıdaki kodta, Address sınıf ismidir. name ve city gibi sınıfta yer alan değişkenler sınıfın nitelikleri(attribute) ya da alanlarıdır(field). Fonksiyon tanımlama ile sınıf oluşturma arasındaki benzerliklere dikkat edin. Bu kod bir sınıfı tanımlar fakat aslında onun bir örneğini oluşturur. Kod bilgisauara bir adresin hangi alanlarının olacağını ve başlangıç değerlerinin ne olacağını söyler. Fonksiyonlardan ve değişkenlerden farklı olarak, sınıf isimleri büyük harfle başlamalıdır. Küçük harfle başlamak da mümkün olduğu halde, bu güzel bir gelenek olarak düşünülmemektedir. Programcılar sık sık sınıfları çizelge haline getirirler, ve bu ders için bir çizelge şu şekildedir: Sınıf ismi üstte ve her özellik onun altında isimleriyle listelenmektedir. Her özelliğin yanında string veya integer gibi veri türleri yazmaktadır. Kod bir adres bile belirlese, bir adresin oluşturulmadığına dikkat edin. Tıpkı fonksiyon gibi, fonksiyonun tanımlanmış olması onun oluşturulduğu anlamına gelmez. Sınıf oluşturmak ve alanları belirlemek için, alttaki örneğe bakın: # Adres oluştur homeAddress=Address() # Adresteki alanları belirle homeAddress.name="John Smith" homeAddress.line1="701 N. C Street" homeAddress.line2="Carver Science Building" homeAddress.city="Indianola" homeAddress.state="IA" homeAddress.zip="50125" address sınıfının bir örneği 9. satırda oluşturulmuştur. Sınıf isminin kullanıldığına ve parantezlerle devam ettiğine dikkat edin. Değişken ismi normal değişken adlandırma kurallarına uyacak şekilde her şey olabilir. Sınıfta alanları belirlemek için, program nokta operatörü kullanmak zorundadır. Bu operatör homeAddress ile alan arasındaki noktadır. Atamanın sol kısmı normal gibi görünmektedir. 12-17 arası astırlar her alana değer atarlar. Çok yaygın yapılan bir hata ise sınıflarla çalışırken değişken ismini belirtmeyi unutmaktır. Sadece bir address oluşturulduysa, bilgisayarın şehri "Indianola" pşaral belirlerken bu adresi kullanacağını varsaymak normaldir. Ancak bu durum böyle değildir. ıkinci bir adres de oluşturulabilir ve ikisinin de alanları kullanılabilir. Önceki örneklerin devamı olarak alttaki kodu inceleyin: # Başka bir adres oluştur vacationHomeAddress=Address() # Adreslerdeki alanları belirle vacationHomeAddress.name="John Smith" vacationHomeAddress.line1="1122 Main Street" vacationHomeAddress.line2="" vacationHomeAddress.city="Panama City Beach" vacationHomeAddress.state="FL" vacationHomeAddress.zip="32407" print ("Müşterinin ev adresi şu şehirde: "+homeAddress.city) print ("Yazlığı ise şurda: "+vacationHomeAddress.city) 19. satır yeni bir değişken ismine atanan ikinci bir adres oluşturuyor. 22-27 arası satırlar bu yeni sınıf örneği için alanları dolduruyor. 29. satır ev adresinin olduğu şehri gösteriyor, çünkü homeAddress nokta operatöründen önce gözüküyor. 30. satır yazlığın adresini gösteriyor, bunun sebebi de vacationHomeAddress nokta operatöründen önce gözüküyor. Yukarıdaki örneklerde, Address, sınıf olarak çağırılır çünkü o veri nesnesinin yeni bir sınıflandırmasını tanımlıyor. homeAddress ve vacationHomeAddress değişkenleri nesnelere(objects) tekabül ediyor çünkü onlar aslında Address sınıfının örneklerine tekabül ediyor. Nesne için basit bir tanım yapmak gerekirse o, sınıfın bir örneğidir. Çok miktarda veriyi bir sınıfın içerisinde alanlara koymak veriyi bir fonksiyonun içerisine ve dışarısına aktarmayı kolaylaştırıyor.Alttaki kodta, fonksiyon address'i bir parametre olarak alıyor ve onu ekrana yazdırıyor. address'in tüm alanlarındaki parametreleri ayrı ayrı aktarmak gerekli değildir. # Ekrana bir adres yazdır def printAddress(address): print (address.name) # Adreste 1. satır(line1) varsa, onu ekrana yazdır if( len(address.line1) > 0 ): print (address.line1) # Adreste 2. satır(line2) varsa, onu ekrana yazdır if( len(address.line2) > 0 ): print( address.line2 ) print( address.city+", "+address.state+" "+address.zip ) printAddress( homeAddress ) print() printAddress( vacationHomeAddress ) - Bu sınıfın bir örneğini oluşturacak ve niteliklerini belirleyecek bir kod yazın: class Dog(): age=0 name="" weight=0 - Bu sınıfın iki farklı örneğini oluşturan ve ikisine de nitelikleri belirleyen bir kod yazın: class Person(): name="" cellPhone="" email="" - Alttaki kod için, kodun çalışabilmesi için uygun sınıf isminde ve niteliklerde bir sınıf yazın. myBird = Bird() myBird.color="green" myBird.name="Sunny" myBird.breed="Sun Conure" - Basit bir 2 boyutlu oyunda bir karakteri temsil edecek bir sınıf oluşturun. Konum, isim ve güç niteliklerini dahil edin. - Alttaki kod çalışır, fakat doğru değildir. Programcı neyi yanlış yapmıştır? class Person: name="" money=0 nancy=Person() name="Nancy" money=100 - Koda biraz bakın. Çalışmıyor. Hatayı bulabilir misiniz? class Person: name="" money=0 bob = Person() print (bob.name,"has",money,"dollars.") - Hata düzeltilmiş olsa bile, program istenen sonucu yazdırmayacaktır: Bob has 0 dollars. Neden bu durum oluşuyor? 13.1.2 Metotlar Niteliklere ek olarak, sınıflar metotlara(methods) sahip olabilirler. Metot, sınıfın içerisinde var olan fonksiyona denir. Yukarıdaki tekrar kısmında 1. problemden Dog sınıfının örneği olan kodu genişletirsek, alttaki kod köpek havlaması için bir metot eklemektedir. class Dog(): age=0 name="" weight=0 def bark(self): print( "Hav!" ) Metot'un tanımlanması yukarıda 6 ve 7'nci satırlarda yapılmıştır. Bir sınıfta metot tanımlamaları nerdeyse tamamen bir fonksiyon tanımlaması gibi görünmektedir. Büyük farklılık ise parametre olarak self'in 6. satıra eklenmiş olmasıdır. Sınıfların içerisindeki bütün metotlarda ilk parametre self olmalıdır. Bu parametre, fonksiyon onu kullanmasa bile gereklidir. Burda sınıflar için metot oluştururken aklınızda tutmanız için önemli noktalar yer alıyor: - Nitelikler önce listelenmelidir, daha sonra metotlar gelir. - Bütün metotların ilk parametresi self olmalıdır. - Metot tanımlamaları tam bir sekme girintilenir. Metotlar, bir nesnedeki niteliklerin belirtilmesine benzer bir şekilde çağırılabilir. Alttaki kodu inceleyin. myDog = Dog() myDog.name="Spot" myDog.weight=20 myDog.age=3 myDog.bark() 9. satırda köpek(dog) oluşturuluyor. 11-13 satırlarında nesnenin nitelikleri belirleniyor. 14. satır bark (havlama) fonksiyonunu çağırıyor. bark fonksiyonu bir tane parametreye sahip olsa bile, self hiçbir şey aktarmaz. Sebebi ise ilk parametrenin dog nesnesinin kendisine bir referans olarak görülmesidir. Perde arkasında, Python şunun gibi görünen bir çağırma yapar: # Örnek, fakat gerçek anlamda geçerli değil Dog.bark(myDog) bark fonksiyonu herhangi bir niteliğe referans kurmak isterse, bunu self referans değişkenini kullanarak yapar. Örneğin, köpek havladığında köpeğin adını da yazacak şekilde Dog sınıfını değiştirebiliriz. Alttaki kodta, isim niteliğine nokta operatörü ve self referansı kullanılarak erişilebilir. def bark(self): print( "Woof says",self.name ) Nitelikler sıfatlardır, metotlar fiillerdir. Bu sınıf için çizim için alttakine bakılabilir: 13.1.3 Örnek: Top Sınıfı Bu, Python/Pygame'de kullanılarak bir top çizdirebilecek örnek kodtur. Tüm parametreleri bir sınıfta toplamak yönetimi daha kolay bir hale getirir. class Ball(): # --- Sınıf Nitelikleri --- # Topun konumu x=0 y=0 # Top vektörü change_x=0 change_y=0 # Top boyutu size=10 # Top rengi color=[255,255,255] # --- Sınıf Metotları --- def move(self): self.x += self.change_x self.y += self.change_y def draw(self, screen): pygame.draw.circle(screen, self.color, [self.x, self.y], self.size ) Altta, ana program döngüsünden önce top oluşturmak için yer alacak ve niteliklerini atayacak bir kod var: theBall = Ball() theBall.x = 100 theBall.y = 100 theBall.change_x = 2 theBall.change_y = 1 theBall.color = [255,0,0] Bu kod topu hareket ettirmek ve çizmek için ana döngünün içerisine gidecek: theBall.move() theBall.draw(screen) 13.2 Referanslar (References) Alttaki kodu bir inceleyin: class Person: name="" money=0 bob = Person() bob.name="Bob" bob.money=100 Kavramlar hakkında yaşanan sıkça rastlanan bir yanılgı ise nesnelerle çalışırken bob'un Person nesnesi olduğunu varsaymaktır. Durum böyle değildir. bob değişkeni Person nesnesine bir referanstır. Bu, örnek kodumuzun tamamlanması ve çalıştırılmasıyla gösterilebilir: nancy = bob nancy.name="Nancy" print(bob.name,"has",bob.money,"dollars.") print(nancy.name,"has",nancy.money,"dollars.") Eğer bob gerçekten bir nesne olsaydı, 9. satırda nesnenin bir kopyası oluşturulurdu ve iki nesne var olurdu. Programın çıktısı Bob ve Nancy'nin 100 dolara sahip olduğunu söylerdi. Fakat çalıştırdığımızda, program onun yerine şöyle bir çıktı veriyor: Nancy has 100 dollars. Nancy has 100 dollars. bob'un sakladığı şey ise nesnenin referansıdır. Referansın yanısıra, adres, işaretçi(pointer) ya da tanıtıcı değer(handle) olarak da çağırılabilir. Referans, nesnenin saklandığı yerin bilgisayar hafızasındaki adresidir. Bu adres, eğer yazdırılırsa, hegzadesimal(onaltılık) bir sayıdır ve 0x1e504 gibi bir şeye benzer. 9. satır çalıştırıldığında bu adresin işaret ettiği bütün nesne yerine sadece adres kopyalanır. 13.2.1 Fonksiyonlar ve Referanslar Alttaki kod önceden gösterilen diğer örneklerle benzerdir. 1. satır, sayı alan bir fonksiyon oluşturmuştur. money değişkeni gönderilen verinin kopyasıdır. Bu sayıya 100 eklemek 10. satırda bob.money içerisinde saklanan sayıyı değiştirmez. Bu nedenle, 13. satırdaki print ifadesi 200 değil, 100 yazdırır. def giveMoney1(money): money += 100 class Person: name="" money=0 bob = Person() bob.name="Bob" bob.money=100 giveMoney1(bob.money) print(bob.money) Alttaki eklenmiş koda bakın. Bu kod bob.money'in artmasına ve print ifadesinin 200 yazdırmasına neden olur. def giveMoney2(person): person.money += 100 giveMoney2(bob) print(bob.money) Bu neden çalıştı? Çünkü person nesnenin bir hafıza adresini içeriyor, nesnenin aslını içermiyor. Bu, banka hesap numarası olarak düşünülebilir. Fonksiyon banka hesap numarasının bir kopyasına sahip, bütün banka hesabının kopyasına değil. Yani, banka hesap numarasının kopyasına 100 dolar eklemek Bob'un banka hesabını da artırarak dengeliyor. Diziler de aynı yolla çalışır. Diziyi (listeyi) bir parametre olarak alan ve bu dizideki değerleri değiştiren fonksiyon, kodun oluşturduğu aynı diziyi de değiştirir. Dizinin adresi kopyalanmıştır, fakat bütün dizi kopyalanmamıştır. - kedi isimli bir sınıf oluşturun. Ona isim, renk ve ağırlık niteliklerini verin. Ayrıca miyav isimli bir metot verin. - Bu kedi sınıfının bir örneğini oluşturun, nitelikleri belirtin ve miyav metodunu çağırın. - Canavar isimli bir sınıf oluşturun. Ona bir isim niteliği ve güç için bir integer niteliği verin. miktar'ı bir parametre olarak alan ve verilen sayıda gücü azaltan gucuAzalt isimli bir metot oluşturun. Bu metodun içerisinde, güç sıfır olursa hayvanın öldüğünü ekrana yazdıran bir kod bulunsun. 13.3 Yapıcılar Python sınıfları sınıfın örneği her oluşturulduğunda çağırılan özel bir fonksiyona sahiptir. Örneğin, altta Dog bir sınıf ve devamında bu sınıfın örneğini oluşturan bir kod bulunuyor: class Dog() name="" myDog = Dog() Yapıcı (inşa edici,constructor) diye çağırılan bir fonksiyon ekleyerek bir programcı, sınıfın örneklendiği her seferde otomatik olarak çalışan bir kod ekleyebilir. Alttaki yapıcı kod örneğini inceleyin: class Dog(): name="" # Yapıcı # Bu tür bir nesne oluşturulduğunda çağırılır def __init__(self): print("Yeni bir köpek doğdu!") # Bu, köpeği oluşturur myDog = Dog() Yapıcı 6. satırda başlıyor. Yapıcının adı __init__ olmak zorundadır. Yapıcılar ilk parametre olarak self'i almak zorundadır. İstendiği takdirde, yapıcılar daha fazla parametreye sahip olabilir. Program çalıştırıldığında, şunu yazdıracaktır: Yeni bir köpek doğdu! 10. satırda Dog nesnesi oluşturulduğunda, __init__ fonksiyonu otomatik olarak çağırılır ve mesaj ekrana yazdırılır. Yapıcı bir fonksiyon nesnenin başlatılması ve verilerin ayarlanması için kullanılabilir. Örneğin, örnek kodta yer alan name niteliği dog nesnesi oluşturulduktan sonra boş bırakılmıştır. Peki ya programcı bir nesneyi varsayılan bir değerde bırakmayı isterse? Bazı nesneler oluşturuldukları anda değerlerinin olmasına gerek duyarlar. Yapıcı fonksiyon bunu gerçekleştirmek için kullanılabilir. Alttaki kodu inceleyin: class Dog(): name="" # Yapıcı # Bu tür bir nesne oluşturulduğunda çağırılır def __init__(self, newName): self.name = newName # Bu, köpeği oluşturur myDog = Dog("Spot") # Atamanın yapıldığını doğrulamak için ismi ekrana yazdır print(myDog.name) # Bu satır hata verecektir çünkü # içeriye bir isim gönderilmemiştir. herDog = Dog() 6. satırda program newName isimli ek bir parametre edinmiştir. Bu parametrenin değeri Dog sınıfında name niteliğini atamak için kullanılmıştır. Sık yapılan bir hata ise __init__ fonksiyonunun parametresine nitelik ile aynı ismi vermek ve değerlerinin otomatik olarak senkronize olacağını sanmaktır. Bu, gerçekleşmez. class Dog(): name="Rover" # Yapıcı # Bu tür bir nesne oluşturulduğunda çağırılır def __init__(self, name): # Bu "Rover" yazdıracaktır print(self.name) # Bu "Spot" yazdıracaktır print(name) # Bu Dog nesnesini oluşturur myDog = Dog("Spot") Önceki örnekte, ekrana yazdırılan iki farklı değişken var. name adlı değişken 5. satırda metodun bir parametresi olarak oluşturuluyor. Bu metot değişkeni metot bittiği anda silinir. Bu, name isimli nitelikle aynı ismi paylaştığı halde (aynı zamanda örnek değişkeni olarak da bilinir), o tamamen farklı bir değişkendir. self.name değişkeni Dog sınıfının bu özel örneğinin name niteliğini belirtir. Dog sınıfının bu örneği var olduğu müddetçe o da var olacaktır. Bu tekrar için boş çalışma kağıdını indirin: [Microsoft Word DOCX] [Microsoft Word DOC] [PDF] - Sınıf isimleri büyük harfle mi küçük harfle mi başlamalıdır? - Metot isimleri büyük harfle mi küçük harfle mi başlamalıdır? - Nitelik isimleri büyük harfle mi küçük harfle mi başlamalıdır? - Bir sınıfta ilk olarak nitelikler mi metotlar mı listelenmelidir? - Referansın diğer isimleri nelerdir? - Örnek değişkenin diğer ismi nedir? - Sınıfın örneği için kullanılan isim nedir? - Star isimli, her oluşturulduğunda ekrana “Bir yıldız doğdu!” yazdıran bir sınıf oluşturun. - Güç ve isim için nitelikleri barındıran Canavar isimli bir sınıf oluşturun. Bu sınıfa gönderilen parametrelere göre nesnenin gücünü ve ismini atayan bir yapıcı fonksiyon ekleyin. 13.4 Miras (Kalıtım, Inheritance) Sınıfları ve nesneleri kullanmanın bir başka güçlü özelliği miras alma olanağına imkan sağlamalarıdır. Bir sınıf oluşturup bir üst sınıf(ebeveyn)tan tüm nitelikleri ve metotları miras almak mümkündür. Örneğin, bir program Person isimli ve bir kişi için gerekli tüm niteliklere sahip bir sınıf oluşturabilir. Program daha sonra, bu alanlardan miras alan alt sınıflar(çocuklar) oluşturabilir. Bu alt sınıflar daha sonra miras aldıkları sınıfın özellik ve metotlarına ihtiyaçlarına karşılık gelen alanları ve metotları ekleyebilir. Bir ebeveyn sınıf daha genel olmalıdır, alt sınıfın özet versiyonu olmalıdır. Örneği, ana sınıf bir Hayvan olabilirken, alt sınıf bir Köpek olabilir ve bu alt sınıf da Pıtır isimli bir alt sınıfa sahip olabilir. Bu şekilde bir çocuktan ebeveyne bağlantı bir ilişki olarak adlandırılır. Örneğin, yunus bir memeli ailesindendir. Bu ters yönde çalışmaz. Bir memeli hayvan yunus olmak zorunda değildir. Yani Yunus sınıfı asla Memeli sınıfının ebeveyni olmak zorunda değildir. Aynı şekilde, Masa sınıfı da Sandalye sınıfının bir alt sınıfı olmak zorunda değildir çünkü sandalye bir masa değildir. class Person(): name="" class Calisan(Person): job_title="" class Musteri(Person): email="" johnSmith = Person() johnSmith.name = "John Smith" janeEmployee = Calisan() janeEmployee.name = "Jane Employee" janeEmployee.job_title = "Web Developer" bobCustomer = Musteri() bobCustomer.name = "Bob Customer" bobCustomer.email = "send_me@spam.com" 4. ve 7. satırda Person'u parantezlerin arasına alarak, programcı bilgisayara Person'un hem Calisan hem de Musteri sınıflarının ebeveyni olduğunu söylemiştir. Bu programa 14. ve 18. satırlarda name niteliğini atama olanağı sağlamıştır. Ayrıca metotlar da miras alınır. Alttaki kod 3 kez “Kişi oluşturuldu” yazdıracaktır çünkü müşteri ve çalışan sınıfları ebeveyn sınıftan yapıcı fonksiyonu miras alır: class Person(): name="" def __init__(self): print("Kişi oluşturuldu") class Calisan(Person): job_title="" class Musteri(Person): email="" johnSmith = Person() janeEmployee = Calisan() bobCustomer = Musteri() Farklı bir işlevsellik sağlamak için metotlar çocuk sınıf yüzünden iptal edilebilir. Alttaki kodta, çocuk sınıflar kendi yapıcılarına sahiptir, yani ebeveynin sınıfı çalışmayacaktır: class Person(): name="" def __init__(self): print("Person created") class Calisan(Person): job_title="" def __init__(self): print("Çalışan oluşturuldu") class Musteri(Person): email="" def __init__(self): print("Müşteri oluşturuldu") johnSmith = Person() janeEmployee = Calisan() bobCustomer = Musteri() Programcı hem ebeveyn hem de çocuk sınıfının metotlarını çalıştırmak isterse, çocuk direk ebeveynin metodunu çağırabilir: class Person(): name="" def __init__(self): print("Person created") class Calisan(Person): job_title="" def __init__(self): Person.__init__(self) print("Çalışan oluşturuldu") class Musteri(Person): email="" def __init__(self): Person.__init__(self) print("Müşteri oluşturuldu") johnSmith = Person() janeEmployee = Calisan() bobCustomer = Musteri() Alttaki programı oluşturun. Onu kodlamadan önce yazmayı deneyin. Bunu yapmak için boş bir çalışma kağıdı indirin: [Microsoft Word DOCX] [Microsoft Word DOC] [PDF] - Hayvan isimli bir sınıf oluşturan kod yazın: - isim adlı bir nitelik ekleyin. - Hayvan sınıfına yemekye() adlı ve "Munch munch" yazdıran bir metot ekleyin. - Hayvan sınıfına gurultuYap() isimli bir metot ekleyin ve bu metodun “[hayvan ismi] gırrr dedi.” yazmasını sağlayın. - Hayvan sınıfına “Bir hayvan doğdu.” yazdıran yapıcı fonksiyon ekleyin. - Kedi isimli bir sınıf: - Hayvan sınıfını ebeveyn yapın. - Kedi için “[hayvan ismi] miyav dedi.” yazdıran bir gurultuYap() metodu ayarlayın. - Kedi için “Bir kedi doğdu.” yazdıran ve ebeveyn yapıcı fonksiyonunu çağıdan bir yapıcı fonksiyon yazın. - Kopek isimli bir sınıf - Hayvan sınıfını ebeveyn yapın. - Kopek için “[hayvan ismi] hav dedi.” yazdıran bir gurultuYap() metodu ayarlayın. - Kopek için “Bir köpek doğdu.” yazdıran ve ebeveyn yapıcı fonksiyonunu çağıdan bir yapıcı fonksiyon yazın. - Şunları içeren bir ana program yapın: - Bir kedi, iki köpek ve bir hayvan oluşturan kod. - Her hayvan için isim belirleyin. - yemekye() ve gurultuYap() metotlarını her hayvan için çağıran bir kod. You are not logged in. Log in here and track your progress. English version by Paul Vincent Craven Russian version by Vladimir Slav Turkish version by Güray Yildirim Dutch version by Frank Waegeman Portuguese version by Tati Carvalho