Python syntax - classes and instances

Posted by _confused_ on Mon, 29 Nov 2021 13:12:12 +0100

class

Class definition

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  def myfunc(self):
    print("Hello my name is " + self.name)

p1 = Person("Bill", 63)
p1.myfunc()

attribute

Class properties

class Person:
    name = "person"

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


p1 = Person("Bill", 63)
print(Person.name)
print(p1.name)

result

person
Bill

getattr/getattribute

class User:
    def __getattribute__(self, item):
        if (item == "age"):
            return 0

    def __getattr__(self, item):
        if (item == "age"):
            return 99

Private property

class Person:
    def __init__(self, name, age):
        self.__name = name
        self.age = age

    def getName(self):
        return self.__name


p1 = Person("Bill", 63)
print(p1.age)
print(p1.getName()) # Can be accessed by method
print(p1._Person__name) # This way is still accessible
print(p1.__name)  # This way is inaccessible

Dynamic properties

class Person:

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

    @property
    def sex(self):
        return "male"


p1 = Person("Xiao Ming", 63)
print(p1.name)
print(p1.sex)

result

Xiao Ming
 male

It can be seen that

Dynamic properties, like the calculated properties of vue, can access methods like accessing properties.

Data descriptor / non data descriptor

import numbers


# Data descriptor
class IntField:
    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("need int value")
        self.value = value

    def __delete__(self, instance):
        pass


# Non data descriptor
class NoDataField:
    def __get__(self, instance, owner):
        return 4


class User:
    age = IntField()


if __name__ == "__main__":
    u = User()
    u.age = 10

In this way, if the assignment is not a number, an error will be reported

This allows you to limit the type of parameter.

Get order of properties

Two ways to get properties

u = User()
u.age
# Equivalent to
getattr(u, "age")

Gets the priority order of the properties

  1. Calling class__ getattribute__
  2. Data descriptor defined in class or base class
  3. Property user. In the instance__ dict__ ["age"]
  4. Non data descriptor in class
  5. Property of user. Class or base class__ dict__ ["age"]
  6. Call in class__ getattr__ method
  7. Throw AttributeError

View the properties of an instance

class User:
    def __init__(self, name, age):
        self.__name = name
        self.age = age


u = User("Xiao Ming", 16)
print(u.__dict__)  # View the properties of an instance
print(dir(u))  # You can only see the name of the attribute, not the value, but all the attributes you can see
print(User.__dict__)  # View properties of a class
print(dir(User))  # View properties of a class

result

{'_User__name': 'Xiao Ming', 'age': 16}
['_User__name', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age']
{'__module__': '__main__', '__init__': <function User.__init__ at 0x017BD100>, '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None}
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

Classes and objects

class User:
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)

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


u = User("Xiao Ming", 16)
print(getattr(u, "age"))

be careful

__ new__ It is used to generate objects from classes, and to invoke them before creating objects. init__ Not called. __ init__ Is used to initialize properties for an object.

method

Example method

class Person:

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

    def get_age(self):
        return self.age


p1 = Person("Xiao Ming", 63)
print(p1.get_age())

Static method

class Person:
    def __init__(self, name, age):
        self.__name = name
        self.age = age

    @staticmethod
    def getName():
        return "123"


print(Person.getName())  # Can be accessed by method
p1 = Person("Bill", 63)
print(p1.getName())  # Can be accessed by method

be careful

Static methods can be called with classes or examples.

Class method

class Person:

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

    @classmethod
    def getPerson(cls, name, age):
        return cls(name, age)


p1 = Person.getPerson("Xiao Ming", 63)
print(p1.name)

be careful

The main advantage of class methods is that there is no need to write the class name when generating class instances, so that once the class name changes, there is no need to modify the code in the class methods.

Judged to be empty

if (p1 is None):

Determine whether it is an instance of a class

class Person:

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


p1 = Person("Xiao Ming", 63)
print(isinstance(p1, Person))

Delete attribute or object

a = object()
b = a
del a
print(b)
print(a)

result

<object object at 0x017265B0>
Traceback (most recent call last):
  File "main.py", line 5, in <module>
    print(a)
NameError: name 'a' is not defined

We will find that after a is deleted, b is still not destroyed. This is because the counter is + 1 every time it is referenced. Only when the counter is 0 will it be destroyed.

inherit

class Student(Person):

Python classes can inherit more than one class

class C(A,B):

be careful

Try not to use multiple inheritance when developing

super

Basic use

class A:
    def __init__(self) -> None:
        print("A")


class B(A):
    def __init__(self) -> None:
        super().__init__()
        print("B")


if __name__ == "__main__":
    b = B()

result

A
B

Sequence of calls

class A:
    def __init__(self) -> None:
        print("A")
        super().__init__()


class B(A):
    def __init__(self) -> None:
        print("B")
        super().__init__()


class C(A):
    def __init__(self) -> None:
        print("C")
        super().__init__()


class D(B, C):
    def __init__(self) -> None:
        print("D")
        super().__init__()


print(D.__mro__)
d = D()

result

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
D
B
C
A

It can be seen that

The calling sequence is called according to the MRO algorithm Use class__ mro__ You can view the call order Properties and methods are both rules

abstract class

import abc


class A(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def say(self):
        pass


class B(A):
    def __init__(self):
        pass

    def say(self):
        pass


if __name__ == "__main__":
    b = B()

be careful

If B does not implement the abstract method of A, an error will be reported.

Dynamically create classes

def create_class01(ctype):
    if (ctype == "user"):
        class User:
            name = "Xiao Ming"

            def say(self):
                print(f"{self.name} say")

        return User


def create_class02(ctype):
    def say(self):
        print(f"{self.name} say")

    if (ctype == "user"):
        User = type("User", (), {"name": "Xiao Ming", "say": say})
        return User


if __name__ == "__main__":
    User = create_class02("user")
    u = User()
    print(u.name)
    u.say()

result

Xiao Ming
 Xiao Ming say