Объектно-ориентированное программирование Python, API

5 (100%) 7 vote[s]

Python, наряду со многими другими языками программирования, использует объектно-ориентированное программирование (ООП). «Объект» — это отдельный элемент. ООП позволяет создавать классы, которые являются общими формами объектов.  Интерфейс прикладного программирования, или API, — это протокол для связи между различными веб-приложениями или различными компонентами одного и того же приложения. Эти различные компоненты захотят обмениваться информацией друг с другом или выполнять действия в других пространствах, и API-интерфейсы допускают это взаимодействие. Поэтому полезно иметь стандартный язык для того, как будет происходить это общение.

Объектно-ориентированное программирование

Например, класс ‘flight’ определяет все компоненты, которые описывают полет, а также действия, которые должен выполнять полет, например, добавление пассажира. Точно так же класс пассажиров будет представлять общую идею пассажира, определенную именем и, возможно, связанную с рейсом.

Вот простой пример класса Python:

class Flight:
 
      def __init__(self, origin, destination, duration):
          self.origin = origin
          self.destination = destination
          self.duration = duration
      • __init__ это «метод», который является функцией, выполняемой над отдельными объектами.__init__, в частности, это специальный встроенный метод, который описывает, что должно происходить при создании объекта flight.
      • Как правило, методы принимают self в качестве первого аргумента. self относится к объекту, с которым работает. Остальные три аргумента — это просто информация, которая должна храниться о конкретном рейсе. Эта информация хранится в виде «свойств» внутри объекта, используя точечные обозначения.

Вот как класс Flight может быть использован:

# Create flight.
  f = Flight(origin="New York", destination="Paris", duration=540)
 
  # Change the value of a propety.
  f.duration += 10
 
  # Print details about flight.
  print(f.origin)
  print(f.destination)
  print(f.duration)
      • обратите внимание, что в Flight() передается только информация о рейсе ; аргумент self метода __init__ задается автоматически;
      • переменная f типа Flight, так же как любая переменная, может иметь тип str или int.

К Flight class могут быть добавлены дополнительные методы:

class Flight:
 
      # assume same __init__ method
 
      def print_info(self):
          print(f"Flight origin: {self.origin}")
          print(f"Flight destination: {self.destination}")
          print(f"Flight duration: {self.duration}")
 
  def main():
      f1 = Flight(origin="New York", destination="Paris", duration=540)
      f1.print_info()
      • Теперь эта функция распечатки информации о полете может использоваться с любым объектом полета, который может быть создан. Каждый раз self относится к объекту, к которому вызывается метод. В этом примере это f1.

Объектно-ориентированное программирование использует методы, которые также могут принимать дополнительные аргументы и изменять свойства.

def delay(self, amount):
      self.duration += amount
      • Обратите внимание, что объектно-ориентированное программирование использует методы записи, такие как delay и print_info, а также просто идея класса Flight в целом, допускают абстракцию. Класс Flight и все его методы могут использоваться логичным и понятным способом без необходимости знать или даже понимать, как может быть реализован Flight.

Простой Passenger класс …

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

Может быть реализован более сложный класс Flight.

class Flight:
 
      counter = 1
 
      def __init__(self, origin, destination, duration):
 
          # Keep track of id number.
          self.id = Flight.counter
          Flight.counter += 1
 
          # Keep track of passengers.
          self.passengers = []
 
          # Details about flight.
          self.origin = origin
          self.destination = destination
          self.duration = duration
 
      def print_info(self):
          print(f"Flight origin: {self.origin}")
          print(f"Flight destination: {self.destination}")
          print(f"Flight duration: {self.duration}")
 
          print()
          print("Passengers:")
          for passenger in self.passengers:
              print(f"{passenger.name}")
 
      def add_passenger(self, p):
          self.passengers.append(p)
          p.flight_id = self.id
      • Обратите внимание, что счетчик определен вне функции __init__ и не специфичен для отдельных рейсов (он не определен как self.counter). Это означает, что все объекты полета могут видеть одну и ту же переменную счетчика, что позволяет реализовать свойство id, показанное здесь. Подобно базе данных SQL, в которой был столбец идентификатора с автоинкрементом, свойство id рейсов будет автоматически увеличиваться при создании новых объектов полета.
      • Свойство passengers объекта Flights будет списком объектов Passenger.
      • В add_passenger создается p.flight_id, поскольку flight_id не определен в классе __init__ класса Passenger.

Вот как можно использовать более продвинутый класс Flight:

# Create flight.
  f1 = Flight(origin="New York", destination="Paris", duration=540)
 
  # Create passengers.
  alice = Passenger(name="Alice")
  bob = Passenger(name="Bob")
 
  # Add passengers.
  f1.add_passenger(alice)
  f1.add_passenger(bob)
 
  f1.print_info()

Реляционное сопоставление объектов

Объектно-реляционное отображение ( Object-Relational Mapping, или ORM), позволяет сочетать объектно-ориентированное программирование Python и мир реляционных баз данных SQL. С ORM классы, методы и объекты Python становятся инструментами для взаимодействия с базами данных SQL. Для этого будет использоваться пакет Flask-SQLAlchemy.

Базовая настройка, внутри models.py:

from flask_sqlalchemy import SQLAlchemy
 
  db = SQLAlchemy()
 
  class Flight(db.Model):
      __tablename__ = "flights"
      id = db.Column(db.Integer, primary_key=True)
      origin = db.Column(db.String, nullable=False)
      destination = db.Column(db.String, nullable=False)
      duration = db.Column(db.Integer, nullable=False)
 
 
  class Passenger(db.Model):
      __tablename__ = "passengers"
      id = db.Column(db.Integer, primary_key=True)
      name = db.Column(db.String, nullable=False)
      flight_id = db.Column(db.Integer, db.ForeignKey("flights.id"), nullable=False)
      • Для любой таблицы внутри базы данных существует один класс, определенный в models.py.
      • Добавление db.Model в круглые скобки после имен классов указывает на то, что эти классы «наследуются» от db.Model. Детали наследования не важны прямо сейчас; просто это позволяет классу иметь некоторые встроенные отношения с SQLAlchemy для взаимодействия с базой данных.
      • __tablename__ соответствует имени таблицы в базе данных.
      • Каждое свойство определяется как db.Column, который станет столбцом в таблице. Аргументы db.Column естественно аналогичны тем, которые используются для создания таблиц в SQL.
      • Обратите внимание, что flight.id помечен как внешний ключ с использованием рейсов __tablename__, а не имени класса Flight.

Теперь, когда есть определенная структура того, как должны выглядеть таблицы, их можно создавать внутри приложения Flask.

import os
 
  from flask import Flask, render_template, request
 
  # Import table definitions.
  from models import *
 
  app = Flask(__name__)
 
  # Tell Flask what SQLAlchemy databas to use.
  app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("DATABASE_URL")
  app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
 
  # Link the Flask app with the database (no Flask app is actually being run yet).
  db.init_app(app)
 
  def main():
      # Create tables based on each table definition in `models`
      db.create_all()
 
  if __name__ == "__main__":
      # Allows for command line interaction with Flask application
      with app.app_context():
          main()

Версии SQL-запросов на Python

db.create_all() версия Python-Flask-SQLAlchemy для команды CREATESQL.

SQL INSERT

INSERT INTO flights
      (origin, destination, duration)
      VALUES ('New York', 'Paris', 540)

…и Python’s INSERT.

flight = Flight(origin="New York", destination="Paris", duration=540)
  db.session.add(flight)
      • SQlAlchemy автоматически заботится о транзакциях SQL с db.session.

SQL SELECT

SELECT * FROM flights;
  SELECT * FROM flights
      WHERE origin = 'Paris';
  SELECT * FROM flights
      WHERE origin = 'Paris' LIMIT 1;
  SELECT COUNT(*) FROM flights
      WHERE origin = 'Paris';
  SELECT * FROM flights WHERE id = 28;
  SELECT * FROM flights
      ORDER BY origin;
  SELECT * FROM flights
      ORDER by origin DESC;
  SELECT * FROM flights
      WHERE origin != 'Paris';
  SELECT * FROM flights
      WHERE origin LIKE '%a%';
  SELECT * FROM flights
      WHERE origin IN ('Tokyo', 'Paris');
  SELECT * FROm flights
      WHERE origin = "Paris"
      AND duration > 500;
  SELECT * FROm flights
      WHERE origin = "Paris"
      AND duration > 500;
  SELECT * FROM flights JOIN passengers
      ON flights.id = passengers.flight_id;

… и Python’s SELECT:

Flight.query.all()
  Flight.query.fliter_by(origin="Paris").all()
  Flight.query.filter_by(origin="Paris").first()
  Flight.query.filter_by(origin="Paris").count()
  Flight.query.get(28)
  Flight.query.order_by(Flight.origin).all()
  Flight.query.order_by(Flights.origin.desc()).all()
  Flight.query.filter(Flight.origin != "Paris").all()
  Flight.query.filter(Fligiht.origin.like("%a%")).all()
  Flight.query.filter(Flight.origin.in_(["Tokyo", "Paris"])).all()
  Flight.query.filter(and_(Flight.origin == "Paris", Flight.duration > 500)).all()
  Flight.query.filter(or_(Flight.origin == "Paris", Flight.duration > 500)).all()
  db.session.query(Flight, Passenger).filter(Flight.id == Passenger.flight_id).all()

SQL UPDATE

UPDATE flights SET duration = 280
      WHERE id = 6;

… и Python’s UPDATE:

flight = Flight.query.get(6)
  flight.duration = 280

SQL DELETE

DELETE FROM flights WHERE id = 28;

… и Python’s DELETE:

flight = Flight.query.get(28)
  db.ksession.delete(flight)

Некоторые другие разные команды SQL …

COMMIT;

… и их параллели Python.

db.session.commit()

Ранее при импорте данных из файла CSV код SQL должен был записываться непосредственно в файл Python. Теперь SQLAlchemy может позаботиться об этом за кулисами.

import csv
 
  # Same setup code as before.
 
  def main():
      f = open("flights.csv")
      reader = csv.reader(f)
      for origin, destination, duration in reader:
          flight = Flight(origin=origin, destination=destination, duration=duration)
          db.session.add(flight)
          print(f"Added flight from {origin} to {destination} lasting {duration} minutes.")
      db.session.commit()

ORM, интегрированный в веб-приложение

Собирая все вместе, вот то же самое веб-приложение из лекции 3, использующее SQLAlchemy. Обратите внимание, что нет необработанных команд SQL. Мощь ORM, классов и объектов используется для вставки и выбора из базы данных.

from flask import Flask, render_template, request
  from models import *
 
  app = Flask(__name__)
  app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv("DATABASE_URL")
  app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
  db.init_app(app)
 
 
  @app.route("/")
  def index():
      flights = Flight.query.all()
      return render_template("index.html", flights=flights)
 
 
  @app.route("/book", methods=["POST"])
  def book():
      """Book a flight."""
 
      # Get form information.
      name = request.form.get("name")
      try:
          flight_id = int(request.form.get("flight_id"))
      except ValueError:
          return render_template("error.html", message="Invalid flight number.")
 
      # Make sure the flight exists.
      flight = Flight.query.get(flight_id)
      if flight is None:
          return render_template("error.html", message="No such flight with that id.")
 
      # Add passenger.
      passenger = Passenger(name=name, flight_id=flight_id)
      db.session.add(passenger)
      db.session.commit()
      return render_template("success.html")
 
 
  @app.route("/flights")
  def flights():
      """List all flights."""
      flights = Flight.query.all()
      return render_template("flights.html", flights=flights)
 
 
  @app.route("/flights/<int:flight_id>")
  def flight(flight_id):
      """List details about a single flight."""
 
      # Make sure flight exists.
      flight = Flight.query.get(flight_id)
      if flight is None:
          return render_template("error.html", message="No such flight.")
 
      # Get all passengers.
      passengers = Passenger.query.filter_by(flight_id=flight_id).all()
      return render_template("flight.html", flight=flight, passengers=passengers)

Поскольку классы гибкие, любые дополнительные функции, которые могут понадобиться приложению, могут быть встроены в классы. Добавление пассажиров, например, может быть определено как метод в Flight классе (в models.py).

def add_passenger(self, name):
      p = Passenger(name=name, flight_id=self.id)
      db.session.add(p)
      db.session.commit()

Теперь, после проверки того, что полет существует, все, что требуется в функции book файла application.py, следующее:

 flight.add_passenger(name)
      • Теперь пассажиры прямо создаются в приложении. Все это обрабатывается классом Flight.

Отношения

Еще одна мощная особенность ORM — идея отношений. Отношения SQLAlchemy — это простой способ взять одну таблицу и связать ее с другой таблицей, чтобы каждая из них могла ссылаться на другую. Связь устанавливается одной строкой, которая в этом случае будет добавлена в определение класса Flight.

passengers = db.relationship("Passenger", backref="flight", lazy=True)
      • passengers это не колонка, а, скорее, просто отношения. Учитывая объект полета, значения пассажиров могут использоваться, чтобы извлечь всю информацию о пассажире для того полета.
      • backref создает отношения в обратном направлении, от рейса до пассажира.
      • lazy указывает, что информация должна быть получена только тогда, когда она запрашивается.

Когда эти отношения установлены, код в функции flight в application.py приложения для получения списка всех пассажиров чрезвычайно упрощается.

passengers = flight.passengers

Еще раз, SQL SELECT

SELECT * FROM passengers
      WHERE flight_id = 1
  SELECT * FROM flights JOIN passengers
      ON flights.id = passengers.flight_id
      WHERE passengers.name = 'Alice';

… и Python на основе отношений SELECT.

Flight.query.get(1).passengers
  Passenger.query.filter_by(name="Alice").first().flight

API-интерфейсы

JSON

Одним из таких языков является Javascript Object Notation (JSON), который представляет собой простой способ представления информации в удобной для человека и компьютерной форме, чтобы ее можно было передавать между частями веб-приложения.

Пример JSON:

{
      "origin" : {
          "city": "Tokyo",
          "code": "HND"
      },
      "destination": {
          "city": "Shanghai",
          "code": "PVG"
      },
      "duration" : 185,
      "passengers" : ["Alice", "Bob"]
  }
      • Фигурные скобки заключают объект JSON.
      • Контексты объекта JSON делятся на пары ключ-значение.
      • origin и duration сами являются объектами JSON, которые вложены в иерархическую структуру.
      • passengers показывает список, какие могут быть значения.

Часто взаимодействие между двумя API происходит через URL-адрес, в котором указывается, какая именно информация должна быть доступна. Некоторые разные уровни могут быть:

/flights/
  /flights/28/
  /flights/28/passengers/
  /flights/28/passengers/6/

Методы HTTP

Бывают разные способы использования API. Например, можно получить информацию о пассажире, зарегистрировать нового пассажира или изменить регистрационную информацию для рейса.
Метод HTTP-запроса будет соответствовать типу действия, которое должно быть выполнено. Это просто соглашение, которому следуют многие API. Некоторые методы HTTP включают в себя:

      • GET : получить ресурс
      • POST : создать новый ресурс
      • PUT : заменить ресурс
      • PATCH : обновить ресурс
      • DELETE : удалить ресурс

Все эти методы HTTP позволяет использовать библиотека Python Requests.

import requests
 
  def main():
      res = requests.get("https://www.google.com/")
      print(res.text)
  * `res` (response) is the HTTP response that comes from submitting, in this case, a `GET` request to a URL. All the following are also valid:
      * `requests.post(url)`
      * `requests.put(url)`
      * `requests.patch(url)`
      * `requests.delete(url)`
  * `res.text` is the HTML content of the page that is returned from the request.

Пример API

Чтобы продемонстрировать потенциал этих запросов, в следующих примерах будет использоваться Fixer , API обменного курса.

Доступ к API по URL-адресу http://data.fixer.io/api/latest?access_key=apikey&base=EUR&symbols=USD ‘возвращает следующий JSON:

{
      "success": true,
      "timestamp": 1519296206,
      "base": "EUR",
      "date": "2018-07-11",
      "rates": {
          "USD": 1.177482
      }
  }

К этому API можно получить доступ в Python, используя библиотеку запросов.

res = requests.get("http://data.fixer.io/api/latest?access_key=apikey&amp;base=EUR&amp;symbols=USD")
  if res.status_code != 200:
      raise Exception("ERROR: API request unsuccessful.")
  data = res.json()
  print(data)
  * Checking the status code of the HTTP response ensures that the API returned what is expected by the application (a JSON formatted like the one above). As an aside, here are some common HTTP status codes. Generally, a leading 2 indicates a successful response, while a leading 4 indicates a failed request.
      * `200 OK`
      * `201 Created`
      * `400 Bad Request`
      * `403 Forbidden`
      * `404 Not Found`
      * `405 Method Not Allowed`
      * `422 Unprocessable Entity`
  * `res.json()` simply extracts the JSON response and puts into the Python variable `data`.

Он вернул весь необработанный JSON, возвращенный API. Поскольку формат JSON согласован и известен, можно извлечь и отобразить наиболее релевантную информацию.

rate = data["rates"]["USD"]
  print(f"1 EUR is equal to {rate} USD")

Для немного большей гибкости и выбора, какие валюты конвертируются, пользовательский ввод может быть принят следующим образом:

 base = input("First Currency: ")
  other = input("Second Currency: ")
  res = requests.get("http://data.fixer.io/api/latest",
                     params={"access_key": apikey, "base": base, "symbols": other})
      • Какие параметры должны быть переданы params (в данном случае "access_key""base" и "symbols"), определены в документации API.

Создание API

Чтобы реализовать API для повторяющегося примера менеджера рейсов авиакомпании, все, что нужно сделать, это определить маршрут, который возвращает объект JSON, как это делает Fixer.

from flask import Flask, render_template, jsonify, request
 
  # ... other imports, set up code, and routes ...
 
  @app.route("/api/flights/<int:flight_id>")
  def flight_api(flight_id):
      """Return details about a single flight."""
 
      # Make sure flight exists.
      flight = Flight.query.get(flight_id)
      if flight is None:
          return jsonify({"error": "Invalid flight_id"}), 422
 
      # Get all passengers.
      passengers = flight.passengers
      names = []
      for passenger in passengers:
          names.append(passenger.name)
      return jsonify({
              "origin": flight.origin,
              "destination": flight.destination,
              "duration": flight.duration,
              "passengers": names
          })

URL-адрес маршрута четко обозначен как API, и в качестве параметра используется любой идентификатор рейса.

jsonify — это функция, предоставляемая Flask, которая берет словарь Python и преобразует его в JSON.

Если рейс не найден, код состояния HTTP (422) также возвращается вместе с JSON, чтобы указать, что произошла ошибка.

Здесь снова видно удобочитаемость и простоту отношений при поиске информации о пассажирах.

Если в качестве параметра был передан действительный идентификатор рейса, то возвращается объект JSON со всей информацией о рейсе и списком пассажиров (поскольку код состояния не указан, по умолчанию он равен 200).

API-ключи

При использовании больших API часто реализуемой функцией является ограничение скорости. Нежелательно, чтобы пользователи делали большое количество запросов, которые могут перегрузить API или затруднить доступ других пользователей к нему. Чтобы ограничить доступ, пользователи должны сначала получить ключ API (длинную строку), который должен быть предоставлен с любым запросом API. Ключи позволяют отслеживать только отдельных пользователей, например, 100 запросов в час на пользователя.

Читайте больше по теме:

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 703

Популярные записи