【Python】Basics - 类

Posted by 西维蜀黍 on 2019-09-23, Last Modified on 2024-05-02

Class

Creating the Dog Class

Each instance created from the Dog class will store a name and an age, and we’ll give each dog the ability to sit() and roll_over():

class Dog():
		"""A simple attempt to model a dog."""
		
		def __init__(self, name, age):
			"""Initialize name and age attributes.""" 
			self.name = name 
			self.age = age
		
		def sit(self):
			"""Simulate a dog sitting in response to a command."""
      print(self.name.title() + " is now sitting.")

		def roll_over(self):
			"""Simulate rolling over in response to a command.""" 
			print(self.name.title() + " rolled over!")

The init() Method

The init() method at w is a special method Python runs automatically whenever we create a new instance based on the Dog class. This method has two leading underscores and two trailing under-scores, a convention that helps prevent Python’s default method names from conflicting with your method names.

We define the init() method to have three parameters: self, name, and age.

The self parameter is required in the method definition, and it must come first before the other parameters. It must be included in the definition because when Python calls this init() method later (to create an instance of Dog), the method call will automatically pass the self argument. Every method call associated with a class automatically passes self, which is a reference to the instance itself; it gives the individual instance access to the attributes and methods in the class. When we make an instance of Dog, Python will call the init() method from the Dog class. We’ll pass Dog() a name and an age as arguments; self is passed automatically, so we don’t need to pass it. Whenever we want to make an instance from the Dog class, we’ll provide values for only the last two parameters, name and age.

The two variables defined at each have the prefix self. Any variable prefixed with self is available to every method in the class, and we’ll also be able to access these variables through any instance created from the class. self.name = name takes the value stored in the parameter name and stores it in the variable name, which is then attached to the instance being created. The same process happens with self.age = age. Variables that are accessible through instances like this are called attributes.

Making an Instance from a Class

Think of a class as a set of instructions for how to make an instance. The class Dog is a set of instructions that tells Python how to make individual instances representing specific dogs.

Let’s make an instance representing a specific dog:

class Dog():
	--snip--

my_dog = Dog('willie', 6)
print("My dog's name is " + my_dog.name.title() + ".") 
print("My dog is " + str(my_dog.age) + " years old.")

Working with Classes and Instances

The Car Class

Let’s write a new class representing a car. Our class will store information about the kind of car we’re working with, and it will have a method that summarizes this information:

class Car():
		"""A simple attempt to represent a car."""
		
		def init(self, make, model, year):
				"""Initialize attributes to describe a car.""" 
				self.make = make 
        self.model = model 						
				self.year = year

		def get_descriptive_name(self):
				"""Return a neatly formatted descriptive name.""" 
				long_name = str(self.year) + ' ' + self.make + ' ' + self.model 
				return long_name.title()

my_new_car = Car('audi', 'a4', 2016) 
print(my_new_car.get_descriptive_name())

First, in the Car class, we define the init() method with the self parameter first, just like we did before with our Dog class. We also give it three other parameters: make, model, and year. The init() method takes in these parameters and stores them in the attributes that will be associated with instances made from this class. When we make a new Car instance, we’ll need to specify a make, model, and year for our instance.

Second, we define a method called get_descriptive_name() that puts a car’s year, make, and model into one string neatly describing the car. This will spare us from having to print each attribute’s value individually. To work with the attribute values in this method, we use self.make, self.model, and self.year.

Inheritance

After a sub-class inherits a parent class, methods defined in the parent class can be called on the sub-class instance.

Polymorphism - Abstract Class

We can define an abstract class using metaclass=ABCMeta and then define an abstract method by quilifying it as @abstractmethod:

from abc import ABCMeta, abstractmethod


class Pet(object, metaclass=ABCMeta):
    """宠物"""

    def __init__(self, nickname):
        self._nickname = nickname

    @abstractmethod
    def make_voice(self):
        """发出声音"""
        pass


class Dog(Pet):
    """狗"""

    def make_voice(self):
        print('%s: 汪汪汪...' % self._nickname)


class Cat(Pet):
    """猫"""

    def make_voice(self):
        print('%s: 喵...喵...' % self._nickname)


def main():
    pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')]
    for pet in pets:
        pet.make_voice()


if __name__ == '__main__':
    main()

Attributes

Accessing attributes

To access the attributes of an instance, you use dot notation. At v we access the value of my_dog’s attribute name by writing:

my_dog.name

Setting a Default Value for an Attribute

Every attribute in a class needs an initial value, even if that value is 0 or an empty string. In some cases, such as when setting a default value, it makes sense to specify this initial value in the body of the init() method; if you do this for an attribute, you don’t have to include a parameter for that attribute.

Let’s add an attribute called odometer_reading that always starts with a value of 0. We’ll also add a method read_odometer() that helps us read each car’s odometer:

class Car():

		def init(self, make, model, year):
				"""Initialize attributes to describe a car.""" 
				self.make = make 
				self.model = model
				self.year = year 
        self.odometer_reading = 0

		def get_descriptive_name(self):
				--snip--

		def read_odometer(self):
				"""Print a statement showing the car's mileage.""" 
				print("This car has " + str(self.odometer_reading) + " miles on it.")

my_new_car = Car('audi', 'a4', 2016) 
print(my_new_car.get_descriptive_name()) 
my_new_car.read_odometer()

This time Python creates a new attribute called odometer_reading and sets its initial value to 0. We also have a new method called read_odometer() that makes it easy to read a car’s mileage.

Method

Static Methods

We can use @staticmethod to define a static method.

from math import sqrt


class Triangle(object):

    def __init__(self, a, b, c):
        self._a = a
        self._b = b
        self._c = c

    @staticmethod
    def is_valid(a, b, c):
        return a + b > c and b + c > a and a + c > b

    def perimeter(self):
        return self._a + self._b + self._c

    def area(self):
        half = self.perimeter() / 2
        return sqrt(half * (half - self._a) *
                    (half - self._b) * (half - self._c))


def main():
    a, b, c = 3, 4, 5
    # 静态方法和类方法都是通过给类发消息来调用的
    if Triangle.is_valid(a, b, c):
        t = Triangle(a, b, c)
        print(t.perimeter())
        # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
        # print(Triangle.perimeter(t))
        print(t.area())
        # print(Triangle.area(t))
    else:
        print('无法构成三角形.')


if __name__ == '__main__':
    main()

Class Methods

Similar to static methods, we can define class methods in Python.

We should use @classmethod to quilify this method and the first parameter of this class method should be “cls”, which represents the current class.

The class itself is a object as well. Sometimes we call it metadata object.

from time import time, localtime, sleep


class Clock(object):
    """数字时钟"""

    def __init__(self, hour=0, minute=0, second=0):
        self._hour = hour
        self._minute = minute
        self._second = second

    @classmethod
    def now(cls):
        ctime = localtime(time())
        return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)

    def run(self):
        """走字"""
        self._second += 1
        if self._second == 60:
            self._second = 0
            self._minute += 1
            if self._minute == 60:
                self._minute = 0
                self._hour += 1
                if self._hour == 24:
                    self._hour = 0

    def show(self):
        """显示时间"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)


def main():
    # 通过类方法创建对象并获取系统时间
    clock = Clock.now()
    while True:
        print(clock.show())
        sleep(1)
        clock.run()


if __name__ == '__main__':
    main()

Access Modifiers (Visibility)

In C++, Java and C#, we can specify a attribute as private and protected in order not to let the outside world access it.

In Python, the access modifiers only contains “public” and “private”. If you want a attribute being private, you should name it using double underscores ("__") at the beginning.

class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    # AttributeError: 'Test' object has no attribute '__bar'
    test.__bar()
    # AttributeError: 'Test' object has no attribute '__foo'
    print(test.__foo)


if __name__ == "__main__":
    main()

However, Python doesn’t prevent the access to the private attributes and methods strictly. In fact, Python just renames these private attributes and methods. Therefore, if you know the rename rules, you still can access them. To prove that:

class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    test._Test__bar()
    print(test._Test__foo)


if __name__ == "__main__":
    main()

@property Decorator

We can use @property decorator to decorate getter and setter methods, so that we can do some checking staff when accessing the private attributes.

class Person(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 访问器 - getter方法
    @property
    def name(self):
        return self._name

    # 访问器 - getter方法
    @property
    def age(self):
        return self._age

    # 修改器 - setter方法
    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        if self._age <= 16:
            print('%s正在玩飞行棋.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大锤', 12)
    person.play()
    person.age = 22
    person.play()
    # person.name = '白元芳'  # AttributeError: can't set attribute


if __name__ == '__main__':
    main()

Reference