user: How do I use Key Concepts in Blender? Include parameters, examples, warnings and best practices.

assistant: ### Data Access

#### Accessing Data-Blocks

You can access Blender’s data with the Python API in the same way as the animation system or user interface;
this implies that any setting that can be changed via a button can also be changed with Python.
Accessing data from the currently loaded blend-file is done with the module [`bpy.data`](bpy.data.html#module-bpy.data "bpy.data").
It gives access to library data, for example:

```
>>> bpy.data.objects
<bpy_collection[3], BlendDataObjects>
```

```
>>> bpy.data.scenes
<bpy_collection[1], BlendDataScenes>
```

```
>>> bpy.data.materials
<bpy_collection[1], BlendDataMaterials>
```

#### Accessing Collections

You will notice that an index as well as a string can be used to access members of the collection.
Unlike Python dictionaries, both methods are available;
however, the index of a member may change while running Blender.

```
>>> list(bpy.data.objects)
[bpy.data.objects["Cube"], bpy.data.objects["Plane"]]
```

```
>>> bpy.data.objects['Cube']
bpy.data.objects["Cube"]
```

```
>>> bpy.data.objects[0]
bpy.data.objects["Cube"]
```

#### Accessing Attributes

Once you have a data-block, such as a material, object, collection, etc.,
its attributes can be accessed much like you would change a setting using the graphical interface.
In fact, the tooltip for each button also displays the Python attribute
which can help in finding what settings to change in a script.

```
>>> bpy.data.objects[0].name
'Camera'
```

```
>>> bpy.data.scenes["Scene"]
bpy.data.scenes['Scene']
```

```
>>> bpy.data.materials.new("MyMaterial")
bpy.data.materials['MyMaterial']
```

For testing what data to access it’s useful to use the Python Console, which is its own space type.
This supports auto-complete, giving you a fast way to explore the data in your file.

Example of a data path that can be quickly found via the console:

```
>>> bpy.data.scenes[0].render.resolution_percentage
100
>>> bpy.data.scenes[0].objects["Torus"].data.vertices[0].co.x
1.0
```

#### Data Creation/Removal

When you are familiar with other Python APIs you may be surprised that
new data-blocks in the bpy API cannot be created by calling the class:

```
>>> bpy.types.Mesh()
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
TypeError: bpy_struct.__new__(type): expected a single argument
```

This is an intentional part of the API design.
The Blender Python API can’t create Blender data that exists outside the main Blender database
(accessed through [`bpy.data`](bpy.data.html#module-bpy.data "bpy.data")), because this data is managed by Blender (save, load, undo, append, etc).

Data is added and removed via methods on the collections in [`bpy.data`](bpy.data.html#module-bpy.data "bpy.data"), e.g:

```
>>> mesh = bpy.data.meshes.new(name="MyMesh")
>>> print(mesh)
<bpy_struct, Mesh("MyMesh.001")>
```

```
>>> bpy.data.meshes.remove(mesh)
```

#### Custom Properties

Python can access properties on any data-block that has an ID
(data that can be linked in and accessed from [`bpy.data`](bpy.data.html#module-bpy.data "bpy.data")).
When assigning a property, you can pick your own names,
these will be created when needed or overwritten if they already exist.

This data is saved with the blend-file and copied with objects, for example:

```
bpy.context.object["MyOwnProperty"] = 42

if "SomeProp" in bpy.context.object:
    print("Property found")

# Use the get function like a Python dictionary
# which can have a fallback value.
value = bpy.data.scenes["Scene"].get("test_prop", "fallback value")

# Dictionaries can be assigned as long as they only use basic types.
collection = bpy.data.collections.new("MyTestCollection")
collection["MySettings"] = {"foo": 10, "bar": "spam", "baz": {}}

del collection["MySettings"]
```

Note that these properties can only be assigned basic Python types:

- int, float, string
- array of ints or floats
- dictionary (only string keys are supported, values must be basic types too)

These properties are valid outside of Python. They can be animated by curves or used in driver paths.

For a list of types that support custom properties see:
[types supporting custom properties](bpy_types_custom_properties.html#bpy-types-custom-properties).

### Context

While it’s useful to be able to access data directly by name or as a list,
it’s more common to operate on the user’s selection.
The context is always available from `bpy.context` and can be used to get the active object, scene,
tool settings along with many other attributes.

Some common use cases are:

```
>>> bpy.context.object
>>> bpy.context.selected_objects
>>> bpy.context.visible_bones
```

Note that the context is read-only, which means that these values cannot be modified directly.
But they can be changed by running API functions or by using the data API.

So `bpy.context.active_object = obj` will raise an error.
But `bpy.context.view_layer.objects.active = obj` works as expected.

The context attributes change depending on where they are accessed.
The 3D Viewport has different context members than the Python Console,
so take care when accessing context attributes that the user state is known.

See [`bpy.context`](bpy.context.html#module-bpy.context "bpy.context") API reference.

### Operators (Tools)

Operators are tools generally accessed by the user from buttons, menu items or key shortcuts.
From the user perspective they are a tool but Python can run these with its own settings
through the [`bpy.ops`](bpy.ops.html#module-bpy.ops "bpy.ops") module.

Examples:

```
>>> bpy.ops.mesh.flip_normals()
{'FINISHED'}
>>> bpy.ops.mesh.hide(unselected=False)
{'FINISHED'}
>>> bpy.ops.object.transform_apply()
{'FINISHED'}
```

#### Operator Poll()

Many operators have a “poll” function which checks if the cursor
is in a valid area or if the object is in the correct mode (Edit Mode, Weight Paint Mode, etc).
When an operator’s poll function fails within Python, an exception is raised.

For example, calling `bpy.ops.view3d.render_border()` from the console raises the following error:

```
RuntimeError: Operator bpy.ops.view3d.render_border.poll() failed, context is incorrect
```

In this case the context must be the 3D Viewport with an active camera.

To avoid using try-except clauses wherever operators are called, you can call the operators
own `poll()` function to check if it can run the operator in the current context.

```
if bpy.ops.view3d.render_border.poll():
    bpy.ops.view3d.render_border()
```